...

Source file src/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/template_test.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger

     1  package genswagger
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
    14  	plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
    15  	structpb "github.com/golang/protobuf/ptypes/struct"
    16  	"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
    17  	"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
    18  	swagger_options "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"
    19  )
    20  
    21  func crossLinkFixture(f *descriptor.File) *descriptor.File {
    22  	for _, m := range f.Messages {
    23  		m.File = f
    24  	}
    25  	for _, svc := range f.Services {
    26  		svc.File = f
    27  		for _, m := range svc.Methods {
    28  			m.Service = svc
    29  			for _, b := range m.Bindings {
    30  				b.Method = m
    31  				for _, param := range b.PathParams {
    32  					param.Method = m
    33  				}
    34  			}
    35  		}
    36  	}
    37  	return f
    38  }
    39  
    40  func reqFromFile(f *descriptor.File) *plugin.CodeGeneratorRequest {
    41  	return &plugin.CodeGeneratorRequest{
    42  		ProtoFile: []*protodescriptor.FileDescriptorProto{
    43  			f.FileDescriptorProto,
    44  		},
    45  		FileToGenerate: []string{f.GetName()},
    46  	}
    47  }
    48  
    49  func TestMessageToQueryParametersWithEnumAsInt(t *testing.T) {
    50  	type test struct {
    51  		MsgDescs []*protodescriptor.DescriptorProto
    52  		Message  string
    53  		Params   []swaggerParameterObject
    54  	}
    55  
    56  	tests := []test{
    57  		{
    58  			MsgDescs: []*protodescriptor.DescriptorProto{
    59  				&protodescriptor.DescriptorProto{
    60  					Name: proto.String("ExampleMessage"),
    61  					Field: []*protodescriptor.FieldDescriptorProto{
    62  						{
    63  							Name:   proto.String("a"),
    64  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
    65  							Number: proto.Int32(1),
    66  						},
    67  						{
    68  							Name:   proto.String("b"),
    69  							Type:   protodescriptor.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
    70  							Number: proto.Int32(2),
    71  						},
    72  						{
    73  							Name:   proto.String("c"),
    74  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
    75  							Label:  protodescriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(),
    76  							Number: proto.Int32(3),
    77  						},
    78  					},
    79  				},
    80  			},
    81  			Message: "ExampleMessage",
    82  			Params: []swaggerParameterObject{
    83  				swaggerParameterObject{
    84  					Name:     "a",
    85  					In:       "query",
    86  					Required: false,
    87  					Type:     "string",
    88  				},
    89  				swaggerParameterObject{
    90  					Name:     "b",
    91  					In:       "query",
    92  					Required: false,
    93  					Type:     "number",
    94  					Format:   "double",
    95  				},
    96  				swaggerParameterObject{
    97  					Name:             "c",
    98  					In:               "query",
    99  					Required:         false,
   100  					Type:             "array",
   101  					CollectionFormat: "multi",
   102  				},
   103  			},
   104  		},
   105  		{
   106  			MsgDescs: []*protodescriptor.DescriptorProto{
   107  				&protodescriptor.DescriptorProto{
   108  					Name: proto.String("ExampleMessage"),
   109  					Field: []*protodescriptor.FieldDescriptorProto{
   110  						{
   111  							Name:     proto.String("nested"),
   112  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   113  							TypeName: proto.String(".example.Nested"),
   114  							Number:   proto.Int32(1),
   115  						},
   116  					},
   117  				},
   118  				&protodescriptor.DescriptorProto{
   119  					Name: proto.String("Nested"),
   120  					Field: []*protodescriptor.FieldDescriptorProto{
   121  						{
   122  							Name:   proto.String("a"),
   123  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   124  							Number: proto.Int32(1),
   125  						},
   126  						{
   127  							Name:     proto.String("deep"),
   128  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   129  							TypeName: proto.String(".example.Nested.DeepNested"),
   130  							Number:   proto.Int32(2),
   131  						},
   132  					},
   133  					NestedType: []*protodescriptor.DescriptorProto{{
   134  						Name: proto.String("DeepNested"),
   135  						Field: []*protodescriptor.FieldDescriptorProto{
   136  							{
   137  								Name:   proto.String("b"),
   138  								Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   139  								Number: proto.Int32(1),
   140  							},
   141  							{
   142  								Name:     proto.String("c"),
   143  								Type:     protodescriptor.FieldDescriptorProto_TYPE_ENUM.Enum(),
   144  								TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
   145  								Number:   proto.Int32(2),
   146  							},
   147  						},
   148  						EnumType: []*protodescriptor.EnumDescriptorProto{
   149  							{
   150  								Name: proto.String("DeepEnum"),
   151  								Value: []*protodescriptor.EnumValueDescriptorProto{
   152  									{Name: proto.String("FALSE"), Number: proto.Int32(0)},
   153  									{Name: proto.String("TRUE"), Number: proto.Int32(1)},
   154  								},
   155  							},
   156  						},
   157  					}},
   158  				},
   159  			},
   160  			Message: "ExampleMessage",
   161  			Params: []swaggerParameterObject{
   162  				swaggerParameterObject{
   163  					Name:     "nested.a",
   164  					In:       "query",
   165  					Required: false,
   166  					Type:     "string",
   167  				},
   168  				swaggerParameterObject{
   169  					Name:     "nested.deep.b",
   170  					In:       "query",
   171  					Required: false,
   172  					Type:     "string",
   173  				},
   174  				swaggerParameterObject{
   175  					Name:     "nested.deep.c",
   176  					In:       "query",
   177  					Required: false,
   178  					Type:     "integer",
   179  					Enum:     []string{"0", "1"},
   180  					Default:  "0",
   181  				},
   182  			},
   183  		},
   184  	}
   185  
   186  	for _, test := range tests {
   187  		reg := descriptor.NewRegistry()
   188  		reg.SetEnumsAsInts(true)
   189  		msgs := []*descriptor.Message{}
   190  		for _, msgdesc := range test.MsgDescs {
   191  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   192  		}
   193  		file := descriptor.File{
   194  			FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   195  				SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   196  				Name:           proto.String("example.proto"),
   197  				Package:        proto.String("example"),
   198  				Dependency:     []string{},
   199  				MessageType:    test.MsgDescs,
   200  				Service:        []*protodescriptor.ServiceDescriptorProto{},
   201  			},
   202  			GoPkg: descriptor.GoPackage{
   203  				Path: "example.com/path/to/example/example.pb",
   204  				Name: "example_pb",
   205  			},
   206  			Messages: msgs,
   207  		}
   208  		reg.Load(&plugin.CodeGeneratorRequest{
   209  			ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
   210  		})
   211  
   212  		message, err := reg.LookupMsg("", ".example."+test.Message)
   213  		if err != nil {
   214  			t.Fatalf("failed to lookup message: %s", err)
   215  		}
   216  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
   217  		if err != nil {
   218  			t.Fatalf("failed to convert message to query parameters: %s", err)
   219  		}
   220  		// avoid checking Items for array types
   221  		for i := range params {
   222  			params[i].Items = nil
   223  		}
   224  		if !reflect.DeepEqual(params, test.Params) {
   225  			t.Errorf("expected %v, got %v", test.Params, params)
   226  		}
   227  	}
   228  }
   229  
   230  func TestMessageToQueryParameters(t *testing.T) {
   231  	type test struct {
   232  		MsgDescs []*protodescriptor.DescriptorProto
   233  		Message  string
   234  		Params   []swaggerParameterObject
   235  	}
   236  
   237  	tests := []test{
   238  		{
   239  			MsgDescs: []*protodescriptor.DescriptorProto{
   240  				&protodescriptor.DescriptorProto{
   241  					Name: proto.String("ExampleMessage"),
   242  					Field: []*protodescriptor.FieldDescriptorProto{
   243  						{
   244  							Name:   proto.String("a"),
   245  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   246  							Number: proto.Int32(1),
   247  						},
   248  						{
   249  							Name:   proto.String("b"),
   250  							Type:   protodescriptor.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
   251  							Number: proto.Int32(2),
   252  						},
   253  						{
   254  							Name:   proto.String("c"),
   255  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   256  							Label:  protodescriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(),
   257  							Number: proto.Int32(3),
   258  						},
   259  					},
   260  				},
   261  			},
   262  			Message: "ExampleMessage",
   263  			Params: []swaggerParameterObject{
   264  				swaggerParameterObject{
   265  					Name:     "a",
   266  					In:       "query",
   267  					Required: false,
   268  					Type:     "string",
   269  				},
   270  				swaggerParameterObject{
   271  					Name:     "b",
   272  					In:       "query",
   273  					Required: false,
   274  					Type:     "number",
   275  					Format:   "double",
   276  				},
   277  				swaggerParameterObject{
   278  					Name:             "c",
   279  					In:               "query",
   280  					Required:         false,
   281  					Type:             "array",
   282  					CollectionFormat: "multi",
   283  				},
   284  			},
   285  		},
   286  		{
   287  			MsgDescs: []*protodescriptor.DescriptorProto{
   288  				&protodescriptor.DescriptorProto{
   289  					Name: proto.String("ExampleMessage"),
   290  					Field: []*protodescriptor.FieldDescriptorProto{
   291  						{
   292  							Name:     proto.String("nested"),
   293  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   294  							TypeName: proto.String(".example.Nested"),
   295  							Number:   proto.Int32(1),
   296  						},
   297  					},
   298  				},
   299  				&protodescriptor.DescriptorProto{
   300  					Name: proto.String("Nested"),
   301  					Field: []*protodescriptor.FieldDescriptorProto{
   302  						{
   303  							Name:   proto.String("a"),
   304  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   305  							Number: proto.Int32(1),
   306  						},
   307  						{
   308  							Name:     proto.String("deep"),
   309  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   310  							TypeName: proto.String(".example.Nested.DeepNested"),
   311  							Number:   proto.Int32(2),
   312  						},
   313  					},
   314  					NestedType: []*protodescriptor.DescriptorProto{{
   315  						Name: proto.String("DeepNested"),
   316  						Field: []*protodescriptor.FieldDescriptorProto{
   317  							{
   318  								Name:   proto.String("b"),
   319  								Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   320  								Number: proto.Int32(1),
   321  							},
   322  							{
   323  								Name:     proto.String("c"),
   324  								Type:     protodescriptor.FieldDescriptorProto_TYPE_ENUM.Enum(),
   325  								TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
   326  								Number:   proto.Int32(2),
   327  							},
   328  						},
   329  						EnumType: []*protodescriptor.EnumDescriptorProto{
   330  							{
   331  								Name: proto.String("DeepEnum"),
   332  								Value: []*protodescriptor.EnumValueDescriptorProto{
   333  									{Name: proto.String("FALSE"), Number: proto.Int32(0)},
   334  									{Name: proto.String("TRUE"), Number: proto.Int32(1)},
   335  								},
   336  							},
   337  						},
   338  					}},
   339  				},
   340  			},
   341  			Message: "ExampleMessage",
   342  			Params: []swaggerParameterObject{
   343  				swaggerParameterObject{
   344  					Name:     "nested.a",
   345  					In:       "query",
   346  					Required: false,
   347  					Type:     "string",
   348  				},
   349  				swaggerParameterObject{
   350  					Name:     "nested.deep.b",
   351  					In:       "query",
   352  					Required: false,
   353  					Type:     "string",
   354  				},
   355  				swaggerParameterObject{
   356  					Name:     "nested.deep.c",
   357  					In:       "query",
   358  					Required: false,
   359  					Type:     "string",
   360  					Enum:     []string{"FALSE", "TRUE"},
   361  					Default:  "FALSE",
   362  				},
   363  			},
   364  		},
   365  	}
   366  
   367  	for _, test := range tests {
   368  		reg := descriptor.NewRegistry()
   369  		msgs := []*descriptor.Message{}
   370  		for _, msgdesc := range test.MsgDescs {
   371  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   372  		}
   373  		file := descriptor.File{
   374  			FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   375  				SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   376  				Name:           proto.String("example.proto"),
   377  				Package:        proto.String("example"),
   378  				Dependency:     []string{},
   379  				MessageType:    test.MsgDescs,
   380  				Service:        []*protodescriptor.ServiceDescriptorProto{},
   381  			},
   382  			GoPkg: descriptor.GoPackage{
   383  				Path: "example.com/path/to/example/example.pb",
   384  				Name: "example_pb",
   385  			},
   386  			Messages: msgs,
   387  		}
   388  		reg.Load(&plugin.CodeGeneratorRequest{
   389  			ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
   390  		})
   391  
   392  		message, err := reg.LookupMsg("", ".example."+test.Message)
   393  		if err != nil {
   394  			t.Fatalf("failed to lookup message: %s", err)
   395  		}
   396  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
   397  		if err != nil {
   398  			t.Fatalf("failed to convert message to query parameters: %s", err)
   399  		}
   400  		// avoid checking Items for array types
   401  		for i := range params {
   402  			params[i].Items = nil
   403  		}
   404  		if !reflect.DeepEqual(params, test.Params) {
   405  			t.Errorf("expected %v, got %v", test.Params, params)
   406  		}
   407  	}
   408  }
   409  
   410  // TestMessagetoQueryParametersNoRecursive, is a check that cyclical references between messages
   411  //  are not falsely detected given previous known edge-cases.
   412  func TestMessageToQueryParametersNoRecursive(t *testing.T) {
   413  	type test struct {
   414  		MsgDescs []*protodescriptor.DescriptorProto
   415  		Message  string
   416  	}
   417  
   418  	tests := []test{
   419  		// First test:
   420  		// Here is a message that has two of another message adjacent to one another in a nested message.
   421  		// There is no loop but this was previouly falsely flagged as a cycle.
   422  		// Example proto:
   423  		// message NonRecursiveMessage {
   424  		//      string field = 1;
   425  		// }
   426  		// message BaseMessage {
   427  		//      NonRecursiveMessage first = 1;
   428  		//      NonRecursiveMessage second = 2;
   429  		// }
   430  		// message QueryMessage {
   431  		//      BaseMessage first = 1;
   432  		//      string second = 2;
   433  		// }
   434  		{
   435  			MsgDescs: []*protodescriptor.DescriptorProto{
   436  				&protodescriptor.DescriptorProto{
   437  					Name: proto.String("QueryMessage"),
   438  					Field: []*protodescriptor.FieldDescriptorProto{
   439  						{
   440  							Name:     proto.String("first"),
   441  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   442  							TypeName: proto.String(".example.BaseMessage"),
   443  							Number:   proto.Int32(1),
   444  						},
   445  						{
   446  							Name:   proto.String("second"),
   447  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   448  							Number: proto.Int32(2),
   449  						},
   450  					},
   451  				},
   452  				&protodescriptor.DescriptorProto{
   453  					Name: proto.String("BaseMessage"),
   454  					Field: []*protodescriptor.FieldDescriptorProto{
   455  						{
   456  							Name:     proto.String("first"),
   457  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   458  							TypeName: proto.String(".example.NonRecursiveMessage"),
   459  							Number:   proto.Int32(1),
   460  						},
   461  						{
   462  							Name:     proto.String("second"),
   463  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   464  							TypeName: proto.String(".example.NonRecursiveMessage"),
   465  							Number:   proto.Int32(2),
   466  						},
   467  					},
   468  				},
   469  				// Note there is no recursive nature to this message
   470  				&protodescriptor.DescriptorProto{
   471  					Name: proto.String("NonRecursiveMessage"),
   472  					Field: []*protodescriptor.FieldDescriptorProto{
   473  						{
   474  							Name: proto.String("field"),
   475  							//Label:  protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   476  							Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   477  							Number: proto.Int32(1),
   478  						},
   479  					},
   480  				},
   481  			},
   482  			Message: "QueryMessage",
   483  		},
   484  	}
   485  
   486  	for _, test := range tests {
   487  		reg := descriptor.NewRegistry()
   488  		msgs := []*descriptor.Message{}
   489  		for _, msgdesc := range test.MsgDescs {
   490  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   491  		}
   492  		file := descriptor.File{
   493  			FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   494  				SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   495  				Name:           proto.String("example.proto"),
   496  				Package:        proto.String("example"),
   497  				Dependency:     []string{},
   498  				MessageType:    test.MsgDescs,
   499  				Service:        []*protodescriptor.ServiceDescriptorProto{},
   500  			},
   501  			GoPkg: descriptor.GoPackage{
   502  				Path: "example.com/path/to/example/example.pb",
   503  				Name: "example_pb",
   504  			},
   505  			Messages: msgs,
   506  		}
   507  		reg.Load(&plugin.CodeGeneratorRequest{
   508  			ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
   509  		})
   510  
   511  		message, err := reg.LookupMsg("", ".example."+test.Message)
   512  		if err != nil {
   513  			t.Fatalf("failed to lookup message: %s", err)
   514  		}
   515  
   516  		_, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
   517  		if err != nil {
   518  			t.Fatalf("No recursion error should be thrown: %s", err)
   519  		}
   520  	}
   521  }
   522  
   523  // TestMessagetoQueryParametersRecursive, is a check that cyclical references between messages
   524  //  are handled gracefully. The goal is to insure that attempts to add messages with cyclical
   525  //  references to query-parameters returns an error message.
   526  func TestMessageToQueryParametersRecursive(t *testing.T) {
   527  	type test struct {
   528  		MsgDescs []*protodescriptor.DescriptorProto
   529  		Message  string
   530  	}
   531  
   532  	tests := []test{
   533  		// First test:
   534  		// Here we test that a message that references it self through a field will return an error.
   535  		// Example proto:
   536  		// message DirectRecursiveMessage {
   537  		//      DirectRecursiveMessage nested = 1;
   538  		// }
   539  		{
   540  			MsgDescs: []*protodescriptor.DescriptorProto{
   541  				&protodescriptor.DescriptorProto{
   542  					Name: proto.String("DirectRecursiveMessage"),
   543  					Field: []*protodescriptor.FieldDescriptorProto{
   544  						{
   545  							Name:     proto.String("nested"),
   546  							Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   547  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   548  							TypeName: proto.String(".example.DirectRecursiveMessage"),
   549  							Number:   proto.Int32(1),
   550  						},
   551  					},
   552  				},
   553  			},
   554  			Message: "DirectRecursiveMessage",
   555  		},
   556  		// Second test:
   557  		// Here we test that a cycle through multiple messages is detected and that an error is returned.
   558  		// Sample:
   559  		// message Root { NodeMessage nested = 1; }
   560  		// message NodeMessage { CycleMessage nested = 1; }
   561  		// message CycleMessage { Root nested = 1; }
   562  		{
   563  			MsgDescs: []*protodescriptor.DescriptorProto{
   564  				&protodescriptor.DescriptorProto{
   565  					Name: proto.String("RootMessage"),
   566  					Field: []*protodescriptor.FieldDescriptorProto{
   567  						{
   568  							Name:     proto.String("nested"),
   569  							Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   570  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   571  							TypeName: proto.String(".example.NodeMessage"),
   572  							Number:   proto.Int32(1),
   573  						},
   574  					},
   575  				},
   576  				&protodescriptor.DescriptorProto{
   577  					Name: proto.String("NodeMessage"),
   578  					Field: []*protodescriptor.FieldDescriptorProto{
   579  						{
   580  							Name:     proto.String("nested"),
   581  							Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   582  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   583  							TypeName: proto.String(".example.CycleMessage"),
   584  							Number:   proto.Int32(1),
   585  						},
   586  					},
   587  				},
   588  				&protodescriptor.DescriptorProto{
   589  					Name: proto.String("CycleMessage"),
   590  					Field: []*protodescriptor.FieldDescriptorProto{
   591  						{
   592  							Name:     proto.String("nested"),
   593  							Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   594  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   595  							TypeName: proto.String(".example.RootMessage"),
   596  							Number:   proto.Int32(1),
   597  						},
   598  					},
   599  				},
   600  			},
   601  			Message: "RootMessage",
   602  		},
   603  	}
   604  
   605  	for _, test := range tests {
   606  		reg := descriptor.NewRegistry()
   607  		msgs := []*descriptor.Message{}
   608  		for _, msgdesc := range test.MsgDescs {
   609  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   610  		}
   611  		file := descriptor.File{
   612  			FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   613  				SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   614  				Name:           proto.String("example.proto"),
   615  				Package:        proto.String("example"),
   616  				Dependency:     []string{},
   617  				MessageType:    test.MsgDescs,
   618  				Service:        []*protodescriptor.ServiceDescriptorProto{},
   619  			},
   620  			GoPkg: descriptor.GoPackage{
   621  				Path: "example.com/path/to/example/example.pb",
   622  				Name: "example_pb",
   623  			},
   624  			Messages: msgs,
   625  		}
   626  		reg.Load(&plugin.CodeGeneratorRequest{
   627  			ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
   628  		})
   629  
   630  		message, err := reg.LookupMsg("", ".example."+test.Message)
   631  		if err != nil {
   632  			t.Fatalf("failed to lookup message: %s", err)
   633  		}
   634  		_, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
   635  		if err == nil {
   636  			t.Fatalf("It should not be allowed to have recursive query parameters")
   637  		}
   638  	}
   639  }
   640  
   641  func TestMessageToQueryParametersWithJsonName(t *testing.T) {
   642  	type test struct {
   643  		MsgDescs []*protodescriptor.DescriptorProto
   644  		Message  string
   645  		Params   []swaggerParameterObject
   646  	}
   647  
   648  	tests := []test{
   649  		{
   650  			MsgDescs: []*protodescriptor.DescriptorProto{
   651  				&protodescriptor.DescriptorProto{
   652  					Name: proto.String("ExampleMessage"),
   653  					Field: []*protodescriptor.FieldDescriptorProto{
   654  						{
   655  							Name:     proto.String("test_field_a"),
   656  							Type:     protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   657  							Number:   proto.Int32(1),
   658  							JsonName: proto.String("testFieldA"),
   659  						},
   660  					},
   661  				},
   662  			},
   663  			Message: "ExampleMessage",
   664  			Params: []swaggerParameterObject{
   665  				swaggerParameterObject{
   666  					Name:     "testFieldA",
   667  					In:       "query",
   668  					Required: false,
   669  					Type:     "string",
   670  				},
   671  			},
   672  		},
   673  		{
   674  			MsgDescs: []*protodescriptor.DescriptorProto{
   675  				&protodescriptor.DescriptorProto{
   676  					Name: proto.String("SubMessage"),
   677  					Field: []*protodescriptor.FieldDescriptorProto{
   678  						{
   679  							Name:     proto.String("test_field_a"),
   680  							Type:     protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
   681  							Number:   proto.Int32(1),
   682  							JsonName: proto.String("testFieldA"),
   683  						},
   684  					},
   685  				},
   686  				&protodescriptor.DescriptorProto{
   687  					Name: proto.String("ExampleMessage"),
   688  					Field: []*protodescriptor.FieldDescriptorProto{
   689  						{
   690  							Name:     proto.String("sub_message"),
   691  							Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   692  							TypeName: proto.String(".example.SubMessage"),
   693  							Number:   proto.Int32(1),
   694  							JsonName: proto.String("subMessage"),
   695  						},
   696  					},
   697  				},
   698  			},
   699  			Message: "ExampleMessage",
   700  			Params: []swaggerParameterObject{
   701  				swaggerParameterObject{
   702  					Name:     "subMessage.testFieldA",
   703  					In:       "query",
   704  					Required: false,
   705  					Type:     "string",
   706  				},
   707  			},
   708  		},
   709  	}
   710  
   711  	for _, test := range tests {
   712  		reg := descriptor.NewRegistry()
   713  		reg.SetUseJSONNamesForFields(true)
   714  		msgs := []*descriptor.Message{}
   715  		for _, msgdesc := range test.MsgDescs {
   716  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   717  		}
   718  		file := descriptor.File{
   719  			FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   720  				SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   721  				Name:           proto.String("example.proto"),
   722  				Package:        proto.String("example"),
   723  				Dependency:     []string{},
   724  				MessageType:    test.MsgDescs,
   725  				Service:        []*protodescriptor.ServiceDescriptorProto{},
   726  			},
   727  			GoPkg: descriptor.GoPackage{
   728  				Path: "example.com/path/to/example/example.pb",
   729  				Name: "example_pb",
   730  			},
   731  			Messages: msgs,
   732  		}
   733  		reg.Load(&plugin.CodeGeneratorRequest{
   734  			ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
   735  		})
   736  
   737  		message, err := reg.LookupMsg("", ".example."+test.Message)
   738  		if err != nil {
   739  			t.Fatalf("failed to lookup message: %s", err)
   740  		}
   741  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
   742  		if err != nil {
   743  			t.Fatalf("failed to convert message to query parameters: %s", err)
   744  		}
   745  		if !reflect.DeepEqual(params, test.Params) {
   746  			t.Errorf("expected %v, got %v", test.Params, params)
   747  		}
   748  	}
   749  }
   750  
   751  func TestApplyTemplateSimple(t *testing.T) {
   752  	msgdesc := &protodescriptor.DescriptorProto{
   753  		Name: proto.String("ExampleMessage"),
   754  	}
   755  	meth := &protodescriptor.MethodDescriptorProto{
   756  		Name:       proto.String("Example"),
   757  		InputType:  proto.String("ExampleMessage"),
   758  		OutputType: proto.String("ExampleMessage"),
   759  	}
   760  	svc := &protodescriptor.ServiceDescriptorProto{
   761  		Name:   proto.String("ExampleService"),
   762  		Method: []*protodescriptor.MethodDescriptorProto{meth},
   763  	}
   764  	msg := &descriptor.Message{
   765  		DescriptorProto: msgdesc,
   766  	}
   767  	file := descriptor.File{
   768  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   769  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   770  			Name:           proto.String("example.proto"),
   771  			Package:        proto.String("example"),
   772  			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
   773  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc},
   774  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
   775  		},
   776  		GoPkg: descriptor.GoPackage{
   777  			Path: "example.com/path/to/example/example.pb",
   778  			Name: "example_pb",
   779  		},
   780  		Messages: []*descriptor.Message{msg},
   781  		Services: []*descriptor.Service{
   782  			{
   783  				ServiceDescriptorProto: svc,
   784  				Methods: []*descriptor.Method{
   785  					{
   786  						MethodDescriptorProto: meth,
   787  						RequestType:           msg,
   788  						ResponseType:          msg,
   789  						Bindings: []*descriptor.Binding{
   790  							{
   791  								HTTPMethod: "GET",
   792  								Body:       &descriptor.Body{FieldPath: nil},
   793  								PathTmpl: httprule.Template{
   794  									Version:  1,
   795  									OpCodes:  []int{0, 0},
   796  									Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
   797  								},
   798  							},
   799  						},
   800  					},
   801  				},
   802  			},
   803  		},
   804  	}
   805  	reg := descriptor.NewRegistry()
   806  	fileCL := crossLinkFixture(&file)
   807  	err := reg.Load(reqFromFile(fileCL))
   808  	if err != nil {
   809  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
   810  		return
   811  	}
   812  	result, err := applyTemplate(param{File: fileCL, reg: reg})
   813  	if err != nil {
   814  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
   815  		return
   816  	}
   817  	if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
   818  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
   819  	}
   820  	if want, is, name := "", result.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
   821  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
   822  	}
   823  	if want, is, name := ([]string)(nil), result.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
   824  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
   825  	}
   826  	if want, is, name := []string{"application/json"}, result.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
   827  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
   828  	}
   829  	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
   830  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
   831  	}
   832  
   833  	// If there was a failure, print out the input and the json result for debugging.
   834  	if t.Failed() {
   835  		t.Errorf("had: %s", file)
   836  		t.Errorf("got: %s", fmt.Sprint(result))
   837  	}
   838  }
   839  
   840  func TestApplyTemplateMultiService(t *testing.T) {
   841  	msgdesc := &protodescriptor.DescriptorProto{
   842  		Name: proto.String("ExampleMessage"),
   843  	}
   844  	meth := &protodescriptor.MethodDescriptorProto{
   845  		Name:       proto.String("Example"),
   846  		InputType:  proto.String("ExampleMessage"),
   847  		OutputType: proto.String("ExampleMessage"),
   848  	}
   849  
   850  	// Create two services that have the same method name. We will test that the
   851  	// operation IDs are different
   852  	svc := &protodescriptor.ServiceDescriptorProto{
   853  		Name:   proto.String("ExampleService"),
   854  		Method: []*protodescriptor.MethodDescriptorProto{meth},
   855  	}
   856  	svc2 := &protodescriptor.ServiceDescriptorProto{
   857  		Name:   proto.String("OtherService"),
   858  		Method: []*protodescriptor.MethodDescriptorProto{meth},
   859  	}
   860  
   861  	msg := &descriptor.Message{
   862  		DescriptorProto: msgdesc,
   863  	}
   864  	file := descriptor.File{
   865  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   866  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   867  			Name:           proto.String("example.proto"),
   868  			Package:        proto.String("example"),
   869  			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
   870  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc},
   871  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
   872  		},
   873  		GoPkg: descriptor.GoPackage{
   874  			Path: "example.com/path/to/example/example.pb",
   875  			Name: "example_pb",
   876  		},
   877  		Messages: []*descriptor.Message{msg},
   878  		Services: []*descriptor.Service{
   879  			{
   880  				ServiceDescriptorProto: svc,
   881  				Methods: []*descriptor.Method{
   882  					{
   883  						MethodDescriptorProto: meth,
   884  						RequestType:           msg,
   885  						ResponseType:          msg,
   886  						Bindings: []*descriptor.Binding{
   887  							{
   888  								HTTPMethod: "GET",
   889  								Body:       &descriptor.Body{FieldPath: nil},
   890  								PathTmpl: httprule.Template{
   891  									Version:  1,
   892  									OpCodes:  []int{0, 0},
   893  									Template: "/v1/echo",
   894  								},
   895  							},
   896  						},
   897  					},
   898  				},
   899  			},
   900  			{
   901  				ServiceDescriptorProto: svc2,
   902  				Methods: []*descriptor.Method{
   903  					{
   904  						MethodDescriptorProto: meth,
   905  						RequestType:           msg,
   906  						ResponseType:          msg,
   907  						Bindings: []*descriptor.Binding{
   908  							{
   909  								HTTPMethod: "GET",
   910  								Body:       &descriptor.Body{FieldPath: nil},
   911  								PathTmpl: httprule.Template{
   912  									Version:  1,
   913  									OpCodes:  []int{0, 0},
   914  									Template: "/v1/ping",
   915  								},
   916  							},
   917  						},
   918  					},
   919  				},
   920  			},
   921  		},
   922  	}
   923  	reg := descriptor.NewRegistry()
   924  	fileCL := crossLinkFixture(&file)
   925  	err := reg.Load(reqFromFile(fileCL))
   926  	if err != nil {
   927  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
   928  		return
   929  	}
   930  	result, err := applyTemplate(param{File: fileCL, reg: reg})
   931  	if err != nil {
   932  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
   933  		return
   934  	}
   935  
   936  	// Check that the two services have unique operation IDs even though they
   937  	// have the same method name.
   938  	if want, is := "ExampleService_Example", result.Paths["/v1/echo"].Get.OperationID; !reflect.DeepEqual(is, want) {
   939  		t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
   940  	}
   941  	if want, is := "OtherService_Example", result.Paths["/v1/ping"].Get.OperationID; !reflect.DeepEqual(is, want) {
   942  		t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
   943  	}
   944  
   945  	// If there was a failure, print out the input and the json result for debugging.
   946  	if t.Failed() {
   947  		t.Errorf("had: %s", file)
   948  		t.Errorf("got: %s", fmt.Sprint(result))
   949  	}
   950  }
   951  
   952  func TestApplyTemplateOverrideOperationID(t *testing.T) {
   953  	msgdesc := &protodescriptor.DescriptorProto{
   954  		Name: proto.String("ExampleMessage"),
   955  	}
   956  	meth := &protodescriptor.MethodDescriptorProto{
   957  		Name:       proto.String("Example"),
   958  		InputType:  proto.String("ExampleMessage"),
   959  		OutputType: proto.String("ExampleMessage"),
   960  		Options:    &protodescriptor.MethodOptions{},
   961  	}
   962  	swaggerOperation := swagger_options.Operation{
   963  		OperationId: "MyExample",
   964  	}
   965  	if err := proto.SetExtension(proto.Message(meth.Options), swagger_options.E_Openapiv2Operation, &swaggerOperation); err != nil {
   966  		t.Fatalf("proto.SetExtension(MethodDescriptorProto.Options) failed: %v", err)
   967  	}
   968  
   969  	svc := &protodescriptor.ServiceDescriptorProto{
   970  		Name:   proto.String("ExampleService"),
   971  		Method: []*protodescriptor.MethodDescriptorProto{meth},
   972  	}
   973  	msg := &descriptor.Message{
   974  		DescriptorProto: msgdesc,
   975  	}
   976  	file := descriptor.File{
   977  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
   978  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
   979  			Name:           proto.String("example.proto"),
   980  			Package:        proto.String("example"),
   981  			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
   982  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc},
   983  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
   984  		},
   985  		GoPkg: descriptor.GoPackage{
   986  			Path: "example.com/path/to/example/example.pb",
   987  			Name: "example_pb",
   988  		},
   989  		Messages: []*descriptor.Message{msg},
   990  		Services: []*descriptor.Service{
   991  			{
   992  				ServiceDescriptorProto: svc,
   993  				Methods: []*descriptor.Method{
   994  					{
   995  						MethodDescriptorProto: meth,
   996  						RequestType:           msg,
   997  						ResponseType:          msg,
   998  						Bindings: []*descriptor.Binding{
   999  							{
  1000  								HTTPMethod: "GET",
  1001  								Body:       &descriptor.Body{FieldPath: nil},
  1002  								PathTmpl: httprule.Template{
  1003  									Version:  1,
  1004  									OpCodes:  []int{0, 0},
  1005  									Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1006  								},
  1007  							},
  1008  						},
  1009  					},
  1010  				},
  1011  			},
  1012  		},
  1013  	}
  1014  
  1015  	reg := descriptor.NewRegistry()
  1016  	fileCL := crossLinkFixture(&file)
  1017  	err := reg.Load(reqFromFile(fileCL))
  1018  	if err != nil {
  1019  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1020  		return
  1021  	}
  1022  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  1023  	if err != nil {
  1024  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1025  		return
  1026  	}
  1027  	if want, is := "MyExample", result.Paths["/v1/echo"].Get.OperationID; !reflect.DeepEqual(is, want) {
  1028  		t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
  1029  	}
  1030  
  1031  	// If there was a failure, print out the input and the json result for debugging.
  1032  	if t.Failed() {
  1033  		t.Errorf("had: %s", file)
  1034  		t.Errorf("got: %s", fmt.Sprint(result))
  1035  	}
  1036  }
  1037  
  1038  func TestApplyTemplateExtensions(t *testing.T) {
  1039  	msgdesc := &protodescriptor.DescriptorProto{
  1040  		Name: proto.String("ExampleMessage"),
  1041  	}
  1042  	meth := &protodescriptor.MethodDescriptorProto{
  1043  		Name:       proto.String("Example"),
  1044  		InputType:  proto.String("ExampleMessage"),
  1045  		OutputType: proto.String("ExampleMessage"),
  1046  		Options:    &protodescriptor.MethodOptions{},
  1047  	}
  1048  	svc := &protodescriptor.ServiceDescriptorProto{
  1049  		Name:   proto.String("ExampleService"),
  1050  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  1051  	}
  1052  	msg := &descriptor.Message{
  1053  		DescriptorProto: msgdesc,
  1054  	}
  1055  	file := descriptor.File{
  1056  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  1057  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  1058  			Name:           proto.String("example.proto"),
  1059  			Package:        proto.String("example"),
  1060  			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
  1061  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc},
  1062  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  1063  			Options:        &protodescriptor.FileOptions{},
  1064  		},
  1065  		GoPkg: descriptor.GoPackage{
  1066  			Path: "example.com/path/to/example/example.pb",
  1067  			Name: "example_pb",
  1068  		},
  1069  		Messages: []*descriptor.Message{msg},
  1070  		Services: []*descriptor.Service{
  1071  			{
  1072  				ServiceDescriptorProto: svc,
  1073  				Methods: []*descriptor.Method{
  1074  					{
  1075  						MethodDescriptorProto: meth,
  1076  						RequestType:           msg,
  1077  						ResponseType:          msg,
  1078  						Bindings: []*descriptor.Binding{
  1079  							{
  1080  								HTTPMethod: "GET",
  1081  								Body:       &descriptor.Body{FieldPath: nil},
  1082  								PathTmpl: httprule.Template{
  1083  									Version:  1,
  1084  									OpCodes:  []int{0, 0},
  1085  									Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1086  								},
  1087  							},
  1088  						},
  1089  					},
  1090  				},
  1091  			},
  1092  		},
  1093  	}
  1094  	swagger := swagger_options.Swagger{
  1095  		Info: &swagger_options.Info{
  1096  			Title: "test",
  1097  			Extensions: map[string]*structpb.Value{
  1098  				"x-info-extension": &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1099  			},
  1100  		},
  1101  		Extensions: map[string]*structpb.Value{
  1102  			"x-foo": &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1103  			"x-bar": &structpb.Value{Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{
  1104  				Values: []*structpb.Value{{Kind: &structpb.Value_StringValue{StringValue: "baz"}}},
  1105  			}}},
  1106  		},
  1107  		SecurityDefinitions: &swagger_options.SecurityDefinitions{
  1108  			Security: map[string]*swagger_options.SecurityScheme{
  1109  				"somescheme": &swagger_options.SecurityScheme{
  1110  					Extensions: map[string]*structpb.Value{
  1111  						"x-security-baz": &structpb.Value{Kind: &structpb.Value_BoolValue{BoolValue: true}},
  1112  					},
  1113  				},
  1114  			},
  1115  		},
  1116  	}
  1117  	if err := proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), swagger_options.E_Openapiv2Swagger, &swagger); err != nil {
  1118  		t.Fatalf("proto.SetExtension(FileDescriptorProto.Options) failed: %v", err)
  1119  	}
  1120  
  1121  	swaggerOperation := swagger_options.Operation{
  1122  		Responses: map[string]*swagger_options.Response{
  1123  			"200": &swagger_options.Response{
  1124  				Extensions: map[string]*structpb.Value{
  1125  					"x-resp-id": &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "resp1000"}},
  1126  				},
  1127  			},
  1128  		},
  1129  		Extensions: map[string]*structpb.Value{
  1130  			"x-op-foo": &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "baz"}},
  1131  		},
  1132  	}
  1133  	if err := proto.SetExtension(proto.Message(meth.Options), swagger_options.E_Openapiv2Operation, &swaggerOperation); err != nil {
  1134  		t.Fatalf("proto.SetExtension(MethodDescriptorProto.Options) failed: %v", err)
  1135  	}
  1136  	reg := descriptor.NewRegistry()
  1137  	fileCL := crossLinkFixture(&file)
  1138  	err := reg.Load(reqFromFile(fileCL))
  1139  	if err != nil {
  1140  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1141  		return
  1142  	}
  1143  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  1144  	if err != nil {
  1145  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1146  		return
  1147  	}
  1148  	if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  1149  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1150  	}
  1151  	if want, is, name := []extension{
  1152  		{key: "x-bar", value: json.RawMessage("[\n      \"baz\"\n    ]")},
  1153  		{key: "x-foo", value: json.RawMessage("\"bar\"")},
  1154  	}, result.extensions, "Extensions"; !reflect.DeepEqual(is, want) {
  1155  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1156  	}
  1157  
  1158  	var scheme swaggerSecuritySchemeObject
  1159  	for _, v := range result.SecurityDefinitions {
  1160  		scheme = v
  1161  	}
  1162  	if want, is, name := []extension{
  1163  		{key: "x-security-baz", value: json.RawMessage("true")},
  1164  	}, scheme.extensions, "SecurityScheme.Extensions"; !reflect.DeepEqual(is, want) {
  1165  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1166  	}
  1167  
  1168  	if want, is, name := []extension{
  1169  		{key: "x-info-extension", value: json.RawMessage("\"bar\"")},
  1170  	}, result.Info.extensions, "Info.Extensions"; !reflect.DeepEqual(is, want) {
  1171  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1172  	}
  1173  
  1174  	var operation *swaggerOperationObject
  1175  	var response swaggerResponseObject
  1176  	for _, v := range result.Paths {
  1177  		operation = v.Get
  1178  		response = v.Get.Responses["200"]
  1179  	}
  1180  	if want, is, name := []extension{
  1181  		{key: "x-op-foo", value: json.RawMessage("\"baz\"")},
  1182  	}, operation.extensions, "operation.Extensions"; !reflect.DeepEqual(is, want) {
  1183  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1184  	}
  1185  	if want, is, name := []extension{
  1186  		{key: "x-resp-id", value: json.RawMessage("\"resp1000\"")},
  1187  	}, response.extensions, "response.Extensions"; !reflect.DeepEqual(is, want) {
  1188  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1189  	}
  1190  }
  1191  
  1192  func TestValidateHeaderType(t *testing.T) {
  1193  	type test struct {
  1194  		Type          string
  1195  		Format        string
  1196  		expectedError error
  1197  	}
  1198  	tests := []test{
  1199  		{
  1200  			"string",
  1201  			"date-time",
  1202  			nil,
  1203  		},
  1204  		{
  1205  			"boolean",
  1206  			"",
  1207  			nil,
  1208  		},
  1209  		{
  1210  			"integer",
  1211  			"uint",
  1212  			nil,
  1213  		},
  1214  		{
  1215  			"integer",
  1216  			"uint8",
  1217  			nil,
  1218  		},
  1219  		{
  1220  			"integer",
  1221  			"uint16",
  1222  			nil,
  1223  		},
  1224  		{
  1225  			"integer",
  1226  			"uint32",
  1227  			nil,
  1228  		},
  1229  		{
  1230  			"integer",
  1231  			"uint64",
  1232  			nil,
  1233  		},
  1234  		{
  1235  			"integer",
  1236  			"int",
  1237  			nil,
  1238  		},
  1239  		{
  1240  			"integer",
  1241  			"int8",
  1242  			nil,
  1243  		},
  1244  		{
  1245  			"integer",
  1246  			"int16",
  1247  			nil,
  1248  		},
  1249  		{
  1250  			"integer",
  1251  			"int32",
  1252  			nil,
  1253  		},
  1254  		{
  1255  			"integer",
  1256  			"int64",
  1257  			nil,
  1258  		},
  1259  		{
  1260  			"integer",
  1261  			"float64",
  1262  			errors.New("the provided format \"float64\" is not a valid extension of the type \"integer\""),
  1263  		},
  1264  		{
  1265  			"integer",
  1266  			"uuid",
  1267  			errors.New("the provided format \"uuid\" is not a valid extension of the type \"integer\""),
  1268  		},
  1269  		{
  1270  			"number",
  1271  			"uint",
  1272  			nil,
  1273  		},
  1274  		{
  1275  			"number",
  1276  			"uint8",
  1277  			nil,
  1278  		},
  1279  		{
  1280  			"number",
  1281  			"uint16",
  1282  			nil,
  1283  		},
  1284  		{
  1285  			"number",
  1286  			"uint32",
  1287  			nil,
  1288  		},
  1289  		{
  1290  			"number",
  1291  			"uint64",
  1292  			nil,
  1293  		},
  1294  		{
  1295  			"number",
  1296  			"int",
  1297  			nil,
  1298  		},
  1299  		{
  1300  			"number",
  1301  			"int8",
  1302  			nil,
  1303  		},
  1304  		{
  1305  			"number",
  1306  			"int16",
  1307  			nil,
  1308  		},
  1309  		{
  1310  			"number",
  1311  			"int32",
  1312  			nil,
  1313  		},
  1314  		{
  1315  			"number",
  1316  			"int64",
  1317  			nil,
  1318  		},
  1319  		{
  1320  			"number",
  1321  			"float",
  1322  			nil,
  1323  		},
  1324  		{
  1325  			"number",
  1326  			"float32",
  1327  			nil,
  1328  		},
  1329  		{
  1330  			"number",
  1331  			"float64",
  1332  			nil,
  1333  		},
  1334  		{
  1335  			"number",
  1336  			"complex64",
  1337  			nil,
  1338  		},
  1339  		{
  1340  			"number",
  1341  			"complex128",
  1342  			nil,
  1343  		},
  1344  		{
  1345  			"number",
  1346  			"double",
  1347  			nil,
  1348  		},
  1349  		{
  1350  			"number",
  1351  			"byte",
  1352  			nil,
  1353  		},
  1354  		{
  1355  			"number",
  1356  			"rune",
  1357  			nil,
  1358  		},
  1359  		{
  1360  			"number",
  1361  			"uintptr",
  1362  			nil,
  1363  		},
  1364  		{
  1365  			"number",
  1366  			"date",
  1367  			errors.New("the provided format \"date\" is not a valid extension of the type \"number\""),
  1368  		},
  1369  		{
  1370  			"array",
  1371  			"",
  1372  			errors.New("the provided header type \"array\" is not supported"),
  1373  		},
  1374  		{
  1375  			"foo",
  1376  			"",
  1377  			errors.New("the provided header type \"foo\" is not supported"),
  1378  		},
  1379  	}
  1380  	for _, v := range tests {
  1381  		err := validateHeaderTypeAndFormat(v.Type, v.Format)
  1382  
  1383  		if v.expectedError == nil {
  1384  			if err != nil {
  1385  				t.Errorf("unexpected error %v", err)
  1386  			}
  1387  		} else {
  1388  			if err == nil {
  1389  				t.Fatal("expected header error not returned")
  1390  			}
  1391  			if err.Error() != v.expectedError.Error() {
  1392  				t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
  1393  			}
  1394  		}
  1395  	}
  1396  
  1397  }
  1398  
  1399  func TestValidateDefaultValueType(t *testing.T) {
  1400  	type test struct {
  1401  		Type          string
  1402  		Value         string
  1403  		Format        string
  1404  		expectedError error
  1405  	}
  1406  	tests := []test{
  1407  		{
  1408  			"string",
  1409  			`"string"`,
  1410  			"",
  1411  			nil,
  1412  		},
  1413  		{
  1414  			"string",
  1415  			"\"2012-11-01T22:08:41+00:00\"",
  1416  			"date-time",
  1417  			nil,
  1418  		},
  1419  		{
  1420  			"string",
  1421  			"\"2012-11-01\"",
  1422  			"date",
  1423  			nil,
  1424  		},
  1425  		{
  1426  			"string",
  1427  			"0",
  1428  			"",
  1429  			errors.New("the provided default value \"0\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
  1430  		},
  1431  		{
  1432  			"string",
  1433  			"false",
  1434  			"",
  1435  			errors.New("the provided default value \"false\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
  1436  		},
  1437  		{
  1438  			"boolean",
  1439  			"true",
  1440  			"",
  1441  			nil,
  1442  		},
  1443  		{
  1444  			"boolean",
  1445  			"0",
  1446  			"",
  1447  			errors.New("the provided default value \"0\" does not match provider type \"boolean\""),
  1448  		},
  1449  		{
  1450  			"boolean",
  1451  			`"string"`,
  1452  			"",
  1453  			errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"boolean\""),
  1454  		},
  1455  		{
  1456  			"number",
  1457  			"1.2",
  1458  			"",
  1459  			nil,
  1460  		},
  1461  		{
  1462  			"number",
  1463  			"123",
  1464  			"",
  1465  			nil,
  1466  		},
  1467  		{
  1468  			"number",
  1469  			"nan",
  1470  			"",
  1471  			errors.New("the provided number \"nan\" is not a valid JSON number"),
  1472  		},
  1473  		{
  1474  			"number",
  1475  			"NaN",
  1476  			"",
  1477  			errors.New("the provided number \"NaN\" is not a valid JSON number"),
  1478  		},
  1479  		{
  1480  			"number",
  1481  			"-459.67",
  1482  			"",
  1483  			nil,
  1484  		},
  1485  		{
  1486  			"number",
  1487  			"inf",
  1488  			"",
  1489  			errors.New("the provided number \"inf\" is not a valid JSON number"),
  1490  		},
  1491  		{
  1492  			"number",
  1493  			"infinity",
  1494  			"",
  1495  			errors.New("the provided number \"infinity\" is not a valid JSON number"),
  1496  		},
  1497  		{
  1498  			"number",
  1499  			"Inf",
  1500  			"",
  1501  			errors.New("the provided number \"Inf\" is not a valid JSON number"),
  1502  		},
  1503  		{
  1504  			"number",
  1505  			"Infinity",
  1506  			"",
  1507  			errors.New("the provided number \"Infinity\" is not a valid JSON number"),
  1508  		},
  1509  		{
  1510  			"number",
  1511  			"false",
  1512  			"",
  1513  			errors.New("the provided default value \"false\" does not match provider type \"number\""),
  1514  		},
  1515  		{
  1516  			"number",
  1517  			`"string"`,
  1518  			"",
  1519  			errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"number\""),
  1520  		},
  1521  		{
  1522  			"integer",
  1523  			"2",
  1524  			"",
  1525  			nil,
  1526  		},
  1527  		{
  1528  			"integer",
  1529  			fmt.Sprint(math.MaxInt32),
  1530  			"int32",
  1531  			nil,
  1532  		},
  1533  		{
  1534  			"integer",
  1535  			fmt.Sprint(math.MaxInt32 + 1),
  1536  			"int32",
  1537  			errors.New("the provided default value \"2147483648\" does not match provided format \"int32\""),
  1538  		},
  1539  		{
  1540  			"integer",
  1541  			fmt.Sprint(math.MaxInt64),
  1542  			"int64",
  1543  			nil,
  1544  		},
  1545  		{
  1546  			"integer",
  1547  			"9223372036854775808",
  1548  			"int64",
  1549  			errors.New("the provided default value \"9223372036854775808\" does not match provided format \"int64\""),
  1550  		},
  1551  		{
  1552  			"integer",
  1553  			"18446744073709551615",
  1554  			"uint64",
  1555  			nil,
  1556  		},
  1557  		{
  1558  			"integer",
  1559  			"false",
  1560  			"",
  1561  			errors.New("the provided default value \"false\" does not match provided type \"integer\""),
  1562  		},
  1563  		{
  1564  			"integer",
  1565  			"1.2",
  1566  			"",
  1567  			errors.New("the provided default value \"1.2\" does not match provided type \"integer\""),
  1568  		},
  1569  		{
  1570  			"integer",
  1571  			`"string"`,
  1572  			"",
  1573  			errors.New("the provided default value \"\\\"string\\\"\" does not match provided type \"integer\""),
  1574  		},
  1575  	}
  1576  	for _, v := range tests {
  1577  		err := validateDefaultValueTypeAndFormat(v.Type, v.Value, v.Format)
  1578  
  1579  		if v.expectedError == nil {
  1580  			if err != nil {
  1581  				t.Errorf("unexpected error '%v'", err)
  1582  			}
  1583  		} else {
  1584  			if err == nil {
  1585  				t.Error("expected update error not returned")
  1586  			}
  1587  			if err.Error() != v.expectedError.Error() {
  1588  				t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
  1589  			}
  1590  		}
  1591  	}
  1592  
  1593  }
  1594  
  1595  func TestApplyTemplateHeaders(t *testing.T) {
  1596  	msgdesc := &protodescriptor.DescriptorProto{
  1597  		Name: proto.String("ExampleMessage"),
  1598  	}
  1599  	meth := &protodescriptor.MethodDescriptorProto{
  1600  		Name:       proto.String("Example"),
  1601  		InputType:  proto.String("ExampleMessage"),
  1602  		OutputType: proto.String("ExampleMessage"),
  1603  		Options:    &protodescriptor.MethodOptions{},
  1604  	}
  1605  	svc := &protodescriptor.ServiceDescriptorProto{
  1606  		Name:   proto.String("ExampleService"),
  1607  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  1608  	}
  1609  	msg := &descriptor.Message{
  1610  		DescriptorProto: msgdesc,
  1611  	}
  1612  	file := descriptor.File{
  1613  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  1614  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  1615  			Name:           proto.String("example.proto"),
  1616  			Package:        proto.String("example"),
  1617  			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
  1618  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc},
  1619  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  1620  			Options:        &protodescriptor.FileOptions{},
  1621  		},
  1622  		GoPkg: descriptor.GoPackage{
  1623  			Path: "example.com/path/to/example/example.pb",
  1624  			Name: "example_pb",
  1625  		},
  1626  		Messages: []*descriptor.Message{msg},
  1627  		Services: []*descriptor.Service{
  1628  			{
  1629  				ServiceDescriptorProto: svc,
  1630  				Methods: []*descriptor.Method{
  1631  					{
  1632  						MethodDescriptorProto: meth,
  1633  						RequestType:           msg,
  1634  						ResponseType:          msg,
  1635  						Bindings: []*descriptor.Binding{
  1636  							{
  1637  								HTTPMethod: "GET",
  1638  								Body:       &descriptor.Body{FieldPath: nil},
  1639  								PathTmpl: httprule.Template{
  1640  									Version:  1,
  1641  									OpCodes:  []int{0, 0},
  1642  									Template: "/v1/echo",
  1643  								},
  1644  							},
  1645  						},
  1646  					},
  1647  				},
  1648  			},
  1649  		},
  1650  	}
  1651  
  1652  	swaggerOperation := swagger_options.Operation{
  1653  		Responses: map[string]*swagger_options.Response{
  1654  			"200": &swagger_options.Response{
  1655  				Description: "Testing Headers",
  1656  				Headers: map[string]*swagger_options.Header{
  1657  					"string": {
  1658  						Description: "string header description",
  1659  						Type:        "string",
  1660  						Format:      "uuid",
  1661  						Pattern:     "",
  1662  					},
  1663  					"boolean": {
  1664  						Description: "boolean header description",
  1665  						Type:        "boolean",
  1666  						Default:     "true",
  1667  						Pattern:     "^true|false$",
  1668  					},
  1669  					"integer": {
  1670  						Description: "integer header description",
  1671  						Type:        "integer",
  1672  						Default:     "0",
  1673  						Pattern:     "^[0-9]$",
  1674  					},
  1675  					"number": {
  1676  						Description: "number header description",
  1677  						Type:        "number",
  1678  						Default:     "1.2",
  1679  						Pattern:     "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
  1680  					},
  1681  				},
  1682  			},
  1683  		},
  1684  	}
  1685  	if err := proto.SetExtension(proto.Message(meth.Options), swagger_options.E_Openapiv2Operation, &swaggerOperation); err != nil {
  1686  		t.Fatalf("proto.SetExtension(MethodDescriptorProto.Options) failed: %v", err)
  1687  	}
  1688  	reg := descriptor.NewRegistry()
  1689  	fileCL := crossLinkFixture(&file)
  1690  	err := reg.Load(reqFromFile(fileCL))
  1691  	if err != nil {
  1692  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1693  		return
  1694  	}
  1695  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  1696  	if err != nil {
  1697  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1698  		return
  1699  	}
  1700  	if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  1701  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1702  	}
  1703  
  1704  	var response swaggerResponseObject
  1705  	for _, v := range result.Paths {
  1706  		response = v.Get.Responses["200"]
  1707  	}
  1708  	if want, is, name := []swaggerHeadersObject{
  1709  		{
  1710  			"String": swaggerHeaderObject{
  1711  				Description: "string header description",
  1712  				Type:        "string",
  1713  				Format:      "uuid",
  1714  				Pattern:     "",
  1715  			},
  1716  			"Boolean": swaggerHeaderObject{
  1717  				Description: "boolean header description",
  1718  				Type:        "boolean",
  1719  				Default:     json.RawMessage("true"),
  1720  				Pattern:     "^true|false$",
  1721  			},
  1722  			"Integer": swaggerHeaderObject{
  1723  				Description: "integer header description",
  1724  				Type:        "integer",
  1725  				Default:     json.RawMessage("0"),
  1726  				Pattern:     "^[0-9]$",
  1727  			},
  1728  			"Number": swaggerHeaderObject{
  1729  				Description: "number header description",
  1730  				Type:        "number",
  1731  				Default:     json.RawMessage("1.2"),
  1732  				Pattern:     "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
  1733  			},
  1734  		},
  1735  	}[0], response.Headers, "response.Headers"; !reflect.DeepEqual(is, want) {
  1736  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1737  	}
  1738  }
  1739  
  1740  func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
  1741  	msgdesc := &protodescriptor.DescriptorProto{
  1742  		Name: proto.String("ExampleMessage"),
  1743  		Field: []*protodescriptor.FieldDescriptorProto{
  1744  			{
  1745  				Name:     proto.String("nested"),
  1746  				Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1747  				Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  1748  				TypeName: proto.String("NestedMessage"),
  1749  				Number:   proto.Int32(1),
  1750  			},
  1751  		},
  1752  	}
  1753  	nesteddesc := &protodescriptor.DescriptorProto{
  1754  		Name: proto.String("NestedMessage"),
  1755  		Field: []*protodescriptor.FieldDescriptorProto{
  1756  			{
  1757  				Name:   proto.String("int32"),
  1758  				Label:  protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1759  				Type:   protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
  1760  				Number: proto.Int32(1),
  1761  			},
  1762  			{
  1763  				Name:   proto.String("bool"),
  1764  				Label:  protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1765  				Type:   protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
  1766  				Number: proto.Int32(2),
  1767  			},
  1768  		},
  1769  	}
  1770  	meth := &protodescriptor.MethodDescriptorProto{
  1771  		Name:            proto.String("Echo"),
  1772  		InputType:       proto.String("ExampleMessage"),
  1773  		OutputType:      proto.String("ExampleMessage"),
  1774  		ClientStreaming: proto.Bool(false),
  1775  	}
  1776  	svc := &protodescriptor.ServiceDescriptorProto{
  1777  		Name:   proto.String("ExampleService"),
  1778  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  1779  	}
  1780  
  1781  	meth.ServerStreaming = proto.Bool(false)
  1782  
  1783  	msg := &descriptor.Message{
  1784  		DescriptorProto: msgdesc,
  1785  	}
  1786  	nested := &descriptor.Message{
  1787  		DescriptorProto: nesteddesc,
  1788  	}
  1789  
  1790  	nestedField := &descriptor.Field{
  1791  		Message:              msg,
  1792  		FieldDescriptorProto: msg.GetField()[0],
  1793  	}
  1794  	intField := &descriptor.Field{
  1795  		Message:              nested,
  1796  		FieldDescriptorProto: nested.GetField()[0],
  1797  	}
  1798  	boolField := &descriptor.Field{
  1799  		Message:              nested,
  1800  		FieldDescriptorProto: nested.GetField()[1],
  1801  	}
  1802  	file := descriptor.File{
  1803  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  1804  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  1805  			Name:           proto.String("example.proto"),
  1806  			Package:        proto.String("example"),
  1807  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
  1808  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  1809  		},
  1810  		GoPkg: descriptor.GoPackage{
  1811  			Path: "example.com/path/to/example/example.pb",
  1812  			Name: "example_pb",
  1813  		},
  1814  		Messages: []*descriptor.Message{msg, nested},
  1815  		Services: []*descriptor.Service{
  1816  			{
  1817  				ServiceDescriptorProto: svc,
  1818  				Methods: []*descriptor.Method{
  1819  					{
  1820  						MethodDescriptorProto: meth,
  1821  						RequestType:           msg,
  1822  						ResponseType:          msg,
  1823  						Bindings: []*descriptor.Binding{
  1824  							{
  1825  								HTTPMethod: "POST",
  1826  								PathTmpl: httprule.Template{
  1827  									Version:  1,
  1828  									OpCodes:  []int{0, 0},
  1829  									Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
  1830  								},
  1831  								PathParams: []descriptor.Parameter{
  1832  									{
  1833  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  1834  											{
  1835  												Name:   "nested",
  1836  												Target: nestedField,
  1837  											},
  1838  											{
  1839  												Name:   "int32",
  1840  												Target: intField,
  1841  											},
  1842  										}),
  1843  										Target: intField,
  1844  									},
  1845  								},
  1846  								Body: &descriptor.Body{
  1847  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  1848  										{
  1849  											Name:   "nested",
  1850  											Target: nestedField,
  1851  										},
  1852  										{
  1853  											Name:   "bool",
  1854  											Target: boolField,
  1855  										},
  1856  									}),
  1857  								},
  1858  							},
  1859  						},
  1860  					},
  1861  				},
  1862  			},
  1863  		},
  1864  	}
  1865  	reg := descriptor.NewRegistry()
  1866  	reg.Load(&plugin.CodeGeneratorRequest{ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto}})
  1867  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  1868  	if err != nil {
  1869  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1870  		return
  1871  	}
  1872  	if want, got := "2.0", result.Swagger; !reflect.DeepEqual(got, want) {
  1873  		t.Errorf("applyTemplate(%#v).Swagger = %s want to be %s", file, got, want)
  1874  	}
  1875  	if want, got := "", result.BasePath; !reflect.DeepEqual(got, want) {
  1876  		t.Errorf("applyTemplate(%#v).BasePath = %s want to be %s", file, got, want)
  1877  	}
  1878  	if want, got := ([]string)(nil), result.Schemes; !reflect.DeepEqual(got, want) {
  1879  		t.Errorf("applyTemplate(%#v).Schemes = %s want to be %s", file, got, want)
  1880  	}
  1881  	if want, got := []string{"application/json"}, result.Consumes; !reflect.DeepEqual(got, want) {
  1882  		t.Errorf("applyTemplate(%#v).Consumes = %s want to be %s", file, got, want)
  1883  	}
  1884  	if want, got := []string{"application/json"}, result.Produces; !reflect.DeepEqual(got, want) {
  1885  		t.Errorf("applyTemplate(%#v).Produces = %s want to be %s", file, got, want)
  1886  	}
  1887  
  1888  	// If there was a failure, print out the input and the json result for debugging.
  1889  	if t.Failed() {
  1890  		t.Errorf("had: %s", file)
  1891  		t.Errorf("got: %s", fmt.Sprint(result))
  1892  	}
  1893  }
  1894  
  1895  func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
  1896  	msgdesc := &protodescriptor.DescriptorProto{
  1897  		Name: proto.String("ExampleMessage"),
  1898  		Field: []*protodescriptor.FieldDescriptorProto{
  1899  			{
  1900  				Name:     proto.String("nested"),
  1901  				Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1902  				Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  1903  				TypeName: proto.String("NestedMessage"),
  1904  				Number:   proto.Int32(1),
  1905  			},
  1906  		},
  1907  	}
  1908  	nesteddesc := &protodescriptor.DescriptorProto{
  1909  		Name: proto.String("NestedMessage"),
  1910  		Field: []*protodescriptor.FieldDescriptorProto{
  1911  			{
  1912  				Name:   proto.String("int32"),
  1913  				Label:  protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1914  				Type:   protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
  1915  				Number: proto.Int32(1),
  1916  			},
  1917  			{
  1918  				Name:   proto.String("bool"),
  1919  				Label:  protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1920  				Type:   protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
  1921  				Number: proto.Int32(2),
  1922  			},
  1923  		},
  1924  	}
  1925  	meth := &protodescriptor.MethodDescriptorProto{
  1926  		Name:            proto.String("Echo"),
  1927  		InputType:       proto.String("ExampleMessage"),
  1928  		OutputType:      proto.String("ExampleMessage"),
  1929  		ClientStreaming: proto.Bool(true),
  1930  		ServerStreaming: proto.Bool(true),
  1931  	}
  1932  	svc := &protodescriptor.ServiceDescriptorProto{
  1933  		Name:   proto.String("ExampleService"),
  1934  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  1935  	}
  1936  
  1937  	msg := &descriptor.Message{
  1938  		DescriptorProto: msgdesc,
  1939  	}
  1940  	nested := &descriptor.Message{
  1941  		DescriptorProto: nesteddesc,
  1942  	}
  1943  
  1944  	nestedField := &descriptor.Field{
  1945  		Message:              msg,
  1946  		FieldDescriptorProto: msg.GetField()[0],
  1947  	}
  1948  	intField := &descriptor.Field{
  1949  		Message:              nested,
  1950  		FieldDescriptorProto: nested.GetField()[0],
  1951  	}
  1952  	boolField := &descriptor.Field{
  1953  		Message:              nested,
  1954  		FieldDescriptorProto: nested.GetField()[1],
  1955  	}
  1956  	file := descriptor.File{
  1957  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  1958  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  1959  			Name:           proto.String("example.proto"),
  1960  			Package:        proto.String("example"),
  1961  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
  1962  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  1963  		},
  1964  		GoPkg: descriptor.GoPackage{
  1965  			Path: "example.com/path/to/example/example.pb",
  1966  			Name: "example_pb",
  1967  		},
  1968  		Messages: []*descriptor.Message{msg, nested},
  1969  		Services: []*descriptor.Service{
  1970  			{
  1971  				ServiceDescriptorProto: svc,
  1972  				Methods: []*descriptor.Method{
  1973  					{
  1974  						MethodDescriptorProto: meth,
  1975  						RequestType:           msg,
  1976  						ResponseType:          msg,
  1977  						Bindings: []*descriptor.Binding{
  1978  							{
  1979  								HTTPMethod: "POST",
  1980  								PathTmpl: httprule.Template{
  1981  									Version:  1,
  1982  									OpCodes:  []int{0, 0},
  1983  									Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
  1984  								},
  1985  								PathParams: []descriptor.Parameter{
  1986  									{
  1987  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  1988  											{
  1989  												Name:   "nested",
  1990  												Target: nestedField,
  1991  											},
  1992  											{
  1993  												Name:   "int32",
  1994  												Target: intField,
  1995  											},
  1996  										}),
  1997  										Target: intField,
  1998  									},
  1999  								},
  2000  								Body: &descriptor.Body{
  2001  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2002  										{
  2003  											Name:   "nested",
  2004  											Target: nestedField,
  2005  										},
  2006  										{
  2007  											Name:   "bool",
  2008  											Target: boolField,
  2009  										},
  2010  									}),
  2011  								},
  2012  							},
  2013  						},
  2014  					},
  2015  				},
  2016  			},
  2017  		},
  2018  	}
  2019  	reg := descriptor.NewRegistry()
  2020  	if err := AddStreamError(reg); err != nil {
  2021  		t.Errorf("AddStreamError(%#v) failed with %v; want success", reg, err)
  2022  		return
  2023  	}
  2024  	reg.Load(&plugin.CodeGeneratorRequest{ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto}})
  2025  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2026  	if err != nil {
  2027  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2028  		return
  2029  	}
  2030  
  2031  	// Only ExampleMessage must be present, not NestedMessage
  2032  	if want, got, name := 4, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  2033  		t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2034  	}
  2035  	if _, ok := result.Paths["/v1/echo"].Post.Responses["200"]; !ok {
  2036  		t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.Paths["/v1/echo"].Post.Responses["200"]`)
  2037  	} else {
  2038  		if want, got, name := "A successful response.(streaming responses)", result.Paths["/v1/echo"].Post.Responses["200"].Description, `result.Paths["/v1/echo"].Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
  2039  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2040  		}
  2041  		streamExampleExampleMessage := result.Paths["/v1/echo"].Post.Responses["200"].Schema
  2042  		if want, got, name := "object", streamExampleExampleMessage.Type, `result.Paths["/v1/echo"].Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
  2043  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2044  		}
  2045  		if want, got, name := "Stream result of exampleExampleMessage", streamExampleExampleMessage.Title, `result.Paths["/v1/echo"].Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
  2046  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2047  		}
  2048  		streamExampleExampleMessageProperties := *(streamExampleExampleMessage.Properties)
  2049  		if want, got, name := 2, len(streamExampleExampleMessageProperties), `len(StreamDefinitions["exampleExampleMessage"].Properties)`; !reflect.DeepEqual(got, want) {
  2050  			t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2051  		} else {
  2052  			resultProperty := streamExampleExampleMessageProperties[0]
  2053  			if want, got, name := "result", resultProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  2054  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2055  			}
  2056  			result := resultProperty.Value.(swaggerSchemaObject)
  2057  			if want, got, name := "#/definitions/exampleExampleMessage", result.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(swaggerSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  2058  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2059  			}
  2060  			errorProperty := streamExampleExampleMessageProperties[1]
  2061  			if want, got, name := "error", errorProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  2062  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2063  			}
  2064  			err := errorProperty.Value.(swaggerSchemaObject)
  2065  			if want, got, name := "#/definitions/runtimeStreamError", err.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(swaggerSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  2066  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2067  			}
  2068  		}
  2069  	}
  2070  
  2071  	// If there was a failure, print out the input and the json result for debugging.
  2072  	if t.Failed() {
  2073  		t.Errorf("had: %s", file)
  2074  		t.Errorf("got: %s", fmt.Sprint(result))
  2075  	}
  2076  }
  2077  
  2078  func TestApplyTemplateRequestWithUnusedReferences(t *testing.T) {
  2079  	reqdesc := &protodescriptor.DescriptorProto{
  2080  		Name: proto.String("ExampleMessage"),
  2081  		Field: []*protodescriptor.FieldDescriptorProto{
  2082  			{
  2083  				Name:   proto.String("string"),
  2084  				Label:  protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2085  				Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2086  				Number: proto.Int32(1),
  2087  			},
  2088  		},
  2089  	}
  2090  	respdesc := &protodescriptor.DescriptorProto{
  2091  		Name: proto.String("EmptyMessage"),
  2092  	}
  2093  	meth := &protodescriptor.MethodDescriptorProto{
  2094  		Name:            proto.String("Example"),
  2095  		InputType:       proto.String("ExampleMessage"),
  2096  		OutputType:      proto.String("EmptyMessage"),
  2097  		ClientStreaming: proto.Bool(false),
  2098  		ServerStreaming: proto.Bool(false),
  2099  	}
  2100  	svc := &protodescriptor.ServiceDescriptorProto{
  2101  		Name:   proto.String("ExampleService"),
  2102  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  2103  	}
  2104  
  2105  	req := &descriptor.Message{
  2106  		DescriptorProto: reqdesc,
  2107  	}
  2108  	resp := &descriptor.Message{
  2109  		DescriptorProto: respdesc,
  2110  	}
  2111  	stringField := &descriptor.Field{
  2112  		Message:              req,
  2113  		FieldDescriptorProto: req.GetField()[0],
  2114  	}
  2115  	file := descriptor.File{
  2116  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  2117  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  2118  			Name:           proto.String("example.proto"),
  2119  			Package:        proto.String("example"),
  2120  			MessageType:    []*protodescriptor.DescriptorProto{reqdesc, respdesc},
  2121  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  2122  		},
  2123  		GoPkg: descriptor.GoPackage{
  2124  			Path: "example.com/path/to/example/example.pb",
  2125  			Name: "example_pb",
  2126  		},
  2127  		Messages: []*descriptor.Message{req, resp},
  2128  		Services: []*descriptor.Service{
  2129  			{
  2130  				ServiceDescriptorProto: svc,
  2131  				Methods: []*descriptor.Method{
  2132  					{
  2133  						MethodDescriptorProto: meth,
  2134  						RequestType:           req,
  2135  						ResponseType:          resp,
  2136  						Bindings: []*descriptor.Binding{
  2137  							{
  2138  								HTTPMethod: "GET",
  2139  								PathTmpl: httprule.Template{
  2140  									Version:  1,
  2141  									OpCodes:  []int{0, 0},
  2142  									Template: "/v1/example",
  2143  								},
  2144  							},
  2145  							{
  2146  								HTTPMethod: "POST",
  2147  								PathTmpl: httprule.Template{
  2148  									Version:  1,
  2149  									OpCodes:  []int{0, 0},
  2150  									Template: "/v1/example/{string}",
  2151  								},
  2152  								PathParams: []descriptor.Parameter{
  2153  									{
  2154  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2155  											{
  2156  												Name:   "string",
  2157  												Target: stringField,
  2158  											},
  2159  										}),
  2160  										Target: stringField,
  2161  									},
  2162  								},
  2163  								Body: &descriptor.Body{
  2164  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2165  										{
  2166  											Name:   "string",
  2167  											Target: stringField,
  2168  										},
  2169  									}),
  2170  								},
  2171  							},
  2172  						},
  2173  					},
  2174  				},
  2175  			},
  2176  		},
  2177  	}
  2178  
  2179  	reg := descriptor.NewRegistry()
  2180  	reg.Load(&plugin.CodeGeneratorRequest{ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto}})
  2181  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2182  	if err != nil {
  2183  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2184  		return
  2185  	}
  2186  
  2187  	// Only EmptyMessage must be present, not ExampleMessage
  2188  	if want, got, name := 1, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  2189  		t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2190  	}
  2191  
  2192  	// If there was a failure, print out the input and the json result for debugging.
  2193  	if t.Failed() {
  2194  		t.Errorf("had: %s", file)
  2195  		t.Errorf("got: %s", fmt.Sprint(result))
  2196  	}
  2197  }
  2198  
  2199  func TestApplyTemplateRequestWithBodyQueryParameters(t *testing.T) {
  2200  	bookDesc := &protodescriptor.DescriptorProto{
  2201  		Name: proto.String("Book"),
  2202  		Field: []*protodescriptor.FieldDescriptorProto{
  2203  			{
  2204  				Name:   proto.String("name"),
  2205  				Label:  protodescriptor.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2206  				Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2207  				Number: proto.Int32(1),
  2208  			},
  2209  			{
  2210  				Name:   proto.String("id"),
  2211  				Label:  protodescriptor.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2212  				Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2213  				Number: proto.Int32(2),
  2214  			},
  2215  		},
  2216  	}
  2217  	createDesc := &protodescriptor.DescriptorProto{
  2218  		Name: proto.String("CreateBookRequest"),
  2219  		Field: []*protodescriptor.FieldDescriptorProto{
  2220  			{
  2221  				Name:   proto.String("parent"),
  2222  				Label:  protodescriptor.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2223  				Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2224  				Number: proto.Int32(1),
  2225  			},
  2226  			{
  2227  				Name:     proto.String("book"),
  2228  				Label:    protodescriptor.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2229  				Type:     protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2230  				TypeName: proto.String("Book"),
  2231  				Number:   proto.Int32(2),
  2232  			},
  2233  			{
  2234  				Name:   proto.String("book_id"),
  2235  				Label:  protodescriptor.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2236  				Type:   protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2237  				Number: proto.Int32(3),
  2238  			},
  2239  		},
  2240  	}
  2241  	meth := &protodescriptor.MethodDescriptorProto{
  2242  		Name:       proto.String("CreateBook"),
  2243  		InputType:  proto.String("CreateBookRequest"),
  2244  		OutputType: proto.String("Book"),
  2245  	}
  2246  	svc := &protodescriptor.ServiceDescriptorProto{
  2247  		Name:   proto.String("BookService"),
  2248  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  2249  	}
  2250  
  2251  	bookMsg := &descriptor.Message{
  2252  		DescriptorProto: bookDesc,
  2253  	}
  2254  	createMsg := &descriptor.Message{
  2255  		DescriptorProto: createDesc,
  2256  	}
  2257  
  2258  	parentField := &descriptor.Field{
  2259  		Message:              createMsg,
  2260  		FieldDescriptorProto: createMsg.GetField()[0],
  2261  	}
  2262  	bookField := &descriptor.Field{
  2263  		Message:              createMsg,
  2264  		FieldMessage:         bookMsg,
  2265  		FieldDescriptorProto: createMsg.GetField()[1],
  2266  	}
  2267  	bookIDField := &descriptor.Field{
  2268  		Message:              createMsg,
  2269  		FieldDescriptorProto: createMsg.GetField()[2],
  2270  	}
  2271  
  2272  	createMsg.Fields = []*descriptor.Field{parentField, bookField, bookIDField}
  2273  
  2274  	file := descriptor.File{
  2275  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  2276  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  2277  			Name:           proto.String("book.proto"),
  2278  			MessageType:    []*protodescriptor.DescriptorProto{bookDesc, createDesc},
  2279  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  2280  		},
  2281  		GoPkg: descriptor.GoPackage{
  2282  			Path: "example.com/path/to/book.pb",
  2283  			Name: "book_pb",
  2284  		},
  2285  		Messages: []*descriptor.Message{bookMsg, createMsg},
  2286  		Services: []*descriptor.Service{
  2287  			{
  2288  				ServiceDescriptorProto: svc,
  2289  				Methods: []*descriptor.Method{
  2290  					{
  2291  						MethodDescriptorProto: meth,
  2292  						RequestType:           createMsg,
  2293  						ResponseType:          bookMsg,
  2294  						Bindings: []*descriptor.Binding{
  2295  							{
  2296  								HTTPMethod: "POST",
  2297  								PathTmpl: httprule.Template{
  2298  									Version:  1,
  2299  									OpCodes:  []int{0, 0},
  2300  									Template: "/v1/{parent=publishers/*}/books",
  2301  								},
  2302  								PathParams: []descriptor.Parameter{
  2303  									{
  2304  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2305  											{
  2306  												Name:   "parent",
  2307  												Target: parentField,
  2308  											},
  2309  										}),
  2310  										Target: parentField,
  2311  									},
  2312  								},
  2313  								Body: &descriptor.Body{
  2314  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2315  										{
  2316  											Name:   "book",
  2317  											Target: bookField,
  2318  										},
  2319  									}),
  2320  								},
  2321  							},
  2322  						},
  2323  					},
  2324  				},
  2325  			},
  2326  		},
  2327  	}
  2328  	reg := descriptor.NewRegistry()
  2329  	reg.Load(&plugin.CodeGeneratorRequest{ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto}})
  2330  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2331  	if err != nil {
  2332  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2333  		return
  2334  	}
  2335  
  2336  	if _, ok := result.Paths["/v1/{parent=publishers/*}/books"].Post.Responses["200"]; !ok {
  2337  		t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.Paths["/v1/{parent=publishers/*}/books"].Post.Responses["200"]`)
  2338  	} else {
  2339  		if want, got, name := 3, len(result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters), `len(result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters)`; !reflect.DeepEqual(got, want) {
  2340  			t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2341  		}
  2342  
  2343  		type param struct {
  2344  			Name     string
  2345  			In       string
  2346  			Required bool
  2347  		}
  2348  
  2349  		p0 := result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[0]
  2350  		if want, got, name := (param{"parent", "path", true}), (param{p0.Name, p0.In, p0.Required}), `result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[0]`; !reflect.DeepEqual(got, want) {
  2351  			t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, got, want)
  2352  		}
  2353  		p1 := result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[1]
  2354  		if want, got, name := (param{"body", "body", true}), (param{p1.Name, p1.In, p1.Required}), `result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[1]`; !reflect.DeepEqual(got, want) {
  2355  			t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, got, want)
  2356  		}
  2357  		p2 := result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[2]
  2358  		if want, got, name := (param{"book_id", "query", false}), (param{p2.Name, p2.In, p2.Required}), `result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[1]`; !reflect.DeepEqual(got, want) {
  2359  			t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, got, want)
  2360  		}
  2361  	}
  2362  
  2363  	// If there was a failure, print out the input and the json result for debugging.
  2364  	if t.Failed() {
  2365  		t.Errorf("had: %s", file)
  2366  		t.Errorf("got: %s", fmt.Sprint(result))
  2367  	}
  2368  }
  2369  
  2370  func generateFieldsForJSONReservedName() []*descriptor.Field {
  2371  	fields := make([]*descriptor.Field, 0)
  2372  	fieldName := string("json_name")
  2373  	fieldJSONName := string("jsonNAME")
  2374  	fieldDescriptor := protodescriptor.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName}
  2375  	field := &descriptor.Field{FieldDescriptorProto: &fieldDescriptor}
  2376  	return append(fields, field)
  2377  }
  2378  
  2379  func generateMsgsForJSONReservedName() []*descriptor.Message {
  2380  	result := make([]*descriptor.Message, 0)
  2381  	// The first message, its field is field_abc and its type is NewType
  2382  	// NewType field_abc
  2383  	fieldName := "field_abc"
  2384  	fieldJSONName := "fieldAbc"
  2385  	messageName1 := "message1"
  2386  	messageType := "pkg.a.NewType"
  2387  	pfd := protodescriptor.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName, TypeName: &messageType}
  2388  	result = append(result,
  2389  		&descriptor.Message{
  2390  			DescriptorProto: &protodescriptor.DescriptorProto{
  2391  				Name: &messageName1, Field: []*protodescriptor.FieldDescriptorProto{&pfd},
  2392  			},
  2393  		})
  2394  	// The second message, its name is NewName, its type is string
  2395  	// message NewType {
  2396  	//    string field_newName [json_name = RESERVEDJSONNAME]
  2397  	// }
  2398  	messageName := "NewType"
  2399  	field := "field_newName"
  2400  	fieldJSONName2 := "RESERVEDJSONNAME"
  2401  	pfd2 := protodescriptor.FieldDescriptorProto{Name: &field, JsonName: &fieldJSONName2}
  2402  	result = append(result, &descriptor.Message{
  2403  		DescriptorProto: &protodescriptor.DescriptorProto{
  2404  			Name: &messageName, Field: []*protodescriptor.FieldDescriptorProto{&pfd2},
  2405  		},
  2406  	})
  2407  	return result
  2408  }
  2409  
  2410  func TestTemplateWithJsonCamelCase(t *testing.T) {
  2411  	var tests = []struct {
  2412  		input    string
  2413  		expected string
  2414  	}{
  2415  		{"/test/{test_id}", "/test/{testId}"},
  2416  		{"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1Id}/test2/{test2Id}"},
  2417  		{"/test1/{test1_id}/{test2_id}", "/test1/{test1Id}/{test2Id}"},
  2418  		{"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1Id}/{test2Id}"},
  2419  		{"/test1/{test1_id1_id2}", "/test1/{test1Id1Id2}"},
  2420  		{"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1Id1Id2}/test2/{test2Id3Id4}"},
  2421  		{"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1Id1Id2}/{test2Id3Id4}"},
  2422  		{"test/{a}", "test/{a}"},
  2423  		{"test/{ab}", "test/{ab}"},
  2424  		{"test/{a_a}", "test/{aA}"},
  2425  		{"test/{ab_c}", "test/{abC}"},
  2426  		{"test/{json_name}", "test/{jsonNAME}"},
  2427  		{"test/{field_abc.field_newName}", "test/{fieldAbc.RESERVEDJSONNAME}"},
  2428  	}
  2429  	reg := descriptor.NewRegistry()
  2430  	reg.SetUseJSONNamesForFields(true)
  2431  	for _, data := range tests {
  2432  		actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2433  		if data.expected != actual {
  2434  			t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2435  		}
  2436  	}
  2437  }
  2438  
  2439  func TestTemplateWithoutJsonCamelCase(t *testing.T) {
  2440  	var tests = []struct {
  2441  		input    string
  2442  		expected string
  2443  	}{
  2444  		{"/test/{test_id}", "/test/{test_id}"},
  2445  		{"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1_id}/test2/{test2_id}"},
  2446  		{"/test1/{test1_id}/{test2_id}", "/test1/{test1_id}/{test2_id}"},
  2447  		{"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1_id}/{test2_id}"},
  2448  		{"/test1/{test1_id1_id2}", "/test1/{test1_id1_id2}"},
  2449  		{"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1_id1_id2}/test2/{test2_id3_id4}"},
  2450  		{"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1_id1_id2}/{test2_id3_id4}"},
  2451  		{"test/{a}", "test/{a}"},
  2452  		{"test/{ab}", "test/{ab}"},
  2453  		{"test/{a_a}", "test/{a_a}"},
  2454  		{"test/{json_name}", "test/{json_name}"},
  2455  		{"test/{field_abc.field_newName}", "test/{field_abc.field_newName}"},
  2456  	}
  2457  	reg := descriptor.NewRegistry()
  2458  	reg.SetUseJSONNamesForFields(false)
  2459  	for _, data := range tests {
  2460  		actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2461  		if data.expected != actual {
  2462  			t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2463  		}
  2464  	}
  2465  }
  2466  
  2467  func TestTemplateToSwaggerPath(t *testing.T) {
  2468  	var tests = []struct {
  2469  		input    string
  2470  		expected string
  2471  	}{
  2472  		{"/test", "/test"},
  2473  		{"/{test}", "/{test}"},
  2474  		{"/{test=prefix/*}", "/{test}"},
  2475  		{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
  2476  		{"/{test1}/{test2}", "/{test1}/{test2}"},
  2477  		{"/{test1}/{test2}/", "/{test1}/{test2}/"},
  2478  		{"/{name=prefix/*}", "/{name=prefix/*}"},
  2479  		{"/{name=prefix1/*/prefix2/*}", "/{name=prefix1/*/prefix2/*}"},
  2480  		{"/{user.name=prefix/*}", "/{user.name=prefix/*}"},
  2481  		{"/{user.name=prefix1/*/prefix2/*}", "/{user.name=prefix1/*/prefix2/*}"},
  2482  		{"/{parent=prefix/*}/children", "/{parent=prefix/*}/children"},
  2483  		{"/{name=prefix/*}:customMethod", "/{name=prefix/*}:customMethod"},
  2484  		{"/{name=prefix1/*/prefix2/*}:customMethod", "/{name=prefix1/*/prefix2/*}:customMethod"},
  2485  		{"/{user.name=prefix/*}:customMethod", "/{user.name=prefix/*}:customMethod"},
  2486  		{"/{user.name=prefix1/*/prefix2/*}:customMethod", "/{user.name=prefix1/*/prefix2/*}:customMethod"},
  2487  		{"/{parent=prefix/*}/children:customMethod", "/{parent=prefix/*}/children:customMethod"},
  2488  	}
  2489  	reg := descriptor.NewRegistry()
  2490  	reg.SetUseJSONNamesForFields(false)
  2491  	for _, data := range tests {
  2492  		actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2493  		if data.expected != actual {
  2494  			t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2495  		}
  2496  	}
  2497  	reg.SetUseJSONNamesForFields(true)
  2498  	for _, data := range tests {
  2499  		actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2500  		if data.expected != actual {
  2501  			t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2502  		}
  2503  	}
  2504  }
  2505  
  2506  func BenchmarkTemplateToSwaggerPath(b *testing.B) {
  2507  	const input = "/{user.name=prefix1/*/prefix2/*}:customMethod"
  2508  
  2509  	b.Run("with JSON names", func(b *testing.B) {
  2510  		reg := descriptor.NewRegistry()
  2511  		reg.SetUseJSONNamesForFields(false)
  2512  
  2513  		for i := 0; i < b.N; i++ {
  2514  			_ = templateToSwaggerPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2515  		}
  2516  	})
  2517  
  2518  	b.Run("without JSON names", func(b *testing.B) {
  2519  		reg := descriptor.NewRegistry()
  2520  		reg.SetUseJSONNamesForFields(true)
  2521  
  2522  		for i := 0; i < b.N; i++ {
  2523  			_ = templateToSwaggerPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2524  		}
  2525  	})
  2526  }
  2527  
  2528  func TestResolveFullyQualifiedNameToSwaggerName(t *testing.T) {
  2529  	var tests = []struct {
  2530  		input                string
  2531  		output               string
  2532  		listOfFQMNs          []string
  2533  		useFQNForSwaggerName bool
  2534  	}{
  2535  		{
  2536  			".a.b.C",
  2537  			"C",
  2538  			[]string{
  2539  				".a.b.C",
  2540  			},
  2541  			false,
  2542  		},
  2543  		{
  2544  			".a.b.C",
  2545  			"abC",
  2546  			[]string{
  2547  				".a.C",
  2548  				".a.b.C",
  2549  			},
  2550  			false,
  2551  		},
  2552  		{
  2553  			".a.b.C",
  2554  			"abC",
  2555  			[]string{
  2556  				".C",
  2557  				".a.C",
  2558  				".a.b.C",
  2559  			},
  2560  			false,
  2561  		},
  2562  		{
  2563  			".a.b.C",
  2564  			"a.b.C",
  2565  			[]string{
  2566  				".C",
  2567  				".a.C",
  2568  				".a.b.C",
  2569  			},
  2570  			true,
  2571  		},
  2572  	}
  2573  
  2574  	for _, data := range tests {
  2575  		names := resolveFullyQualifiedNameToSwaggerNames(data.listOfFQMNs, data.useFQNForSwaggerName)
  2576  		output := names[data.input]
  2577  		if output != data.output {
  2578  			t.Errorf("Expected fullyQualifiedNameToSwaggerName(%v) to be %s but got %s",
  2579  				data.input, data.output, output)
  2580  		}
  2581  	}
  2582  }
  2583  
  2584  func TestFQMNtoSwaggerName(t *testing.T) {
  2585  	var tests = []struct {
  2586  		input    string
  2587  		expected string
  2588  	}{
  2589  		{"/test", "/test"},
  2590  		{"/{test}", "/{test}"},
  2591  		{"/{test=prefix/*}", "/{test}"},
  2592  		{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
  2593  		{"/{test1}/{test2}", "/{test1}/{test2}"},
  2594  		{"/{test1}/{test2}/", "/{test1}/{test2}/"},
  2595  	}
  2596  	reg := descriptor.NewRegistry()
  2597  	reg.SetUseJSONNamesForFields(false)
  2598  	for _, data := range tests {
  2599  		actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2600  		if data.expected != actual {
  2601  			t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2602  		}
  2603  	}
  2604  	reg.SetUseJSONNamesForFields(true)
  2605  	for _, data := range tests {
  2606  		actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2607  		if data.expected != actual {
  2608  			t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2609  		}
  2610  	}
  2611  }
  2612  
  2613  func TestSchemaOfField(t *testing.T) {
  2614  	type test struct {
  2615  		field    *descriptor.Field
  2616  		refs     refMap
  2617  		expected swaggerSchemaObject
  2618  	}
  2619  
  2620  	var fieldOptions = new(protodescriptor.FieldOptions)
  2621  	err := proto.SetExtension(fieldOptions, swagger_options.E_Openapiv2Field, &swagger_options.JSONSchema{
  2622  		Title:       "field title",
  2623  		Description: "field description",
  2624  	})
  2625  	if err != nil {
  2626  		t.Errorf("proto.SetExtension() failed with %v; want success", err)
  2627  	}
  2628  
  2629  	tests := []test{
  2630  		{
  2631  			field: &descriptor.Field{
  2632  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2633  					Name: proto.String("primitive_field"),
  2634  					Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2635  				},
  2636  			},
  2637  			refs: make(refMap),
  2638  			expected: swaggerSchemaObject{
  2639  				schemaCore: schemaCore{
  2640  					Type: "string",
  2641  				},
  2642  			},
  2643  		},
  2644  		{
  2645  			field: &descriptor.Field{
  2646  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2647  					Name:  proto.String("repeated_primitive_field"),
  2648  					Type:  protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2649  					Label: protodescriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  2650  				},
  2651  			},
  2652  			refs: make(refMap),
  2653  			expected: swaggerSchemaObject{
  2654  				schemaCore: schemaCore{
  2655  					Type: "array",
  2656  					Items: &swaggerItemsObject{
  2657  						Type: "string",
  2658  					},
  2659  				},
  2660  			},
  2661  		},
  2662  		{
  2663  			field: &descriptor.Field{
  2664  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2665  					Name:     proto.String("wrapped_field"),
  2666  					TypeName: proto.String(".google.protobuf.StringValue"),
  2667  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2668  				},
  2669  			},
  2670  			refs: make(refMap),
  2671  			expected: swaggerSchemaObject{
  2672  				schemaCore: schemaCore{
  2673  					Type: "string",
  2674  				},
  2675  			},
  2676  		},
  2677  		{
  2678  			field: &descriptor.Field{
  2679  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2680  					Name:     proto.String("repeated_wrapped_field"),
  2681  					TypeName: proto.String(".google.protobuf.StringValue"),
  2682  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2683  					Label:    protodescriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  2684  				},
  2685  			},
  2686  			refs: make(refMap),
  2687  			expected: swaggerSchemaObject{
  2688  				schemaCore: schemaCore{
  2689  					Type: "array",
  2690  					Items: &swaggerItemsObject{
  2691  						Type: "string",
  2692  					},
  2693  				},
  2694  			},
  2695  		},
  2696  		{
  2697  			field: &descriptor.Field{
  2698  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2699  					Name:     proto.String("wrapped_field"),
  2700  					TypeName: proto.String(".google.protobuf.BytesValue"),
  2701  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2702  				},
  2703  			},
  2704  			refs: make(refMap),
  2705  			expected: swaggerSchemaObject{
  2706  				schemaCore: schemaCore{
  2707  					Type:   "string",
  2708  					Format: "byte",
  2709  				},
  2710  			},
  2711  		},
  2712  		{
  2713  			field: &descriptor.Field{
  2714  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2715  					Name:     proto.String("wrapped_field"),
  2716  					TypeName: proto.String(".google.protobuf.Int32Value"),
  2717  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2718  				},
  2719  			},
  2720  			refs: make(refMap),
  2721  			expected: swaggerSchemaObject{
  2722  				schemaCore: schemaCore{
  2723  					Type:   "integer",
  2724  					Format: "int32",
  2725  				},
  2726  			},
  2727  		},
  2728  		{
  2729  			field: &descriptor.Field{
  2730  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2731  					Name:     proto.String("wrapped_field"),
  2732  					TypeName: proto.String(".google.protobuf.UInt32Value"),
  2733  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2734  				},
  2735  			},
  2736  			refs: make(refMap),
  2737  			expected: swaggerSchemaObject{
  2738  				schemaCore: schemaCore{
  2739  					Type:   "integer",
  2740  					Format: "int64",
  2741  				},
  2742  			},
  2743  		},
  2744  		{
  2745  			field: &descriptor.Field{
  2746  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2747  					Name:     proto.String("wrapped_field"),
  2748  					TypeName: proto.String(".google.protobuf.Int64Value"),
  2749  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2750  				},
  2751  			},
  2752  			refs: make(refMap),
  2753  			expected: swaggerSchemaObject{
  2754  				schemaCore: schemaCore{
  2755  					Type:   "string",
  2756  					Format: "int64",
  2757  				},
  2758  			},
  2759  		},
  2760  		{
  2761  			field: &descriptor.Field{
  2762  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2763  					Name:     proto.String("wrapped_field"),
  2764  					TypeName: proto.String(".google.protobuf.UInt64Value"),
  2765  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2766  				},
  2767  			},
  2768  			refs: make(refMap),
  2769  			expected: swaggerSchemaObject{
  2770  				schemaCore: schemaCore{
  2771  					Type:   "string",
  2772  					Format: "uint64",
  2773  				},
  2774  			},
  2775  		},
  2776  		{
  2777  			field: &descriptor.Field{
  2778  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2779  					Name:     proto.String("wrapped_field"),
  2780  					TypeName: proto.String(".google.protobuf.FloatValue"),
  2781  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2782  				},
  2783  			},
  2784  			refs: make(refMap),
  2785  			expected: swaggerSchemaObject{
  2786  				schemaCore: schemaCore{
  2787  					Type:   "number",
  2788  					Format: "float",
  2789  				},
  2790  			},
  2791  		},
  2792  		{
  2793  			field: &descriptor.Field{
  2794  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2795  					Name:     proto.String("wrapped_field"),
  2796  					TypeName: proto.String(".google.protobuf.DoubleValue"),
  2797  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2798  				},
  2799  			},
  2800  			refs: make(refMap),
  2801  			expected: swaggerSchemaObject{
  2802  				schemaCore: schemaCore{
  2803  					Type:   "number",
  2804  					Format: "double",
  2805  				},
  2806  			},
  2807  		},
  2808  		{
  2809  			field: &descriptor.Field{
  2810  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2811  					Name:     proto.String("wrapped_field"),
  2812  					TypeName: proto.String(".google.protobuf.BoolValue"),
  2813  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2814  				},
  2815  			},
  2816  			refs: make(refMap),
  2817  			expected: swaggerSchemaObject{
  2818  				schemaCore: schemaCore{
  2819  					Type: "boolean",
  2820  				},
  2821  			},
  2822  		},
  2823  		{
  2824  			field: &descriptor.Field{
  2825  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2826  					Name:     proto.String("wrapped_field"),
  2827  					TypeName: proto.String(".google.protobuf.Struct"),
  2828  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2829  				},
  2830  			},
  2831  			refs: make(refMap),
  2832  			expected: swaggerSchemaObject{
  2833  				schemaCore: schemaCore{
  2834  					Type: "object",
  2835  				},
  2836  			},
  2837  		},
  2838  		{
  2839  			field: &descriptor.Field{
  2840  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2841  					Name:     proto.String("wrapped_field"),
  2842  					TypeName: proto.String(".google.protobuf.Value"),
  2843  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2844  				},
  2845  			},
  2846  			refs: make(refMap),
  2847  			expected: swaggerSchemaObject{
  2848  				schemaCore: schemaCore{
  2849  					Type: "object",
  2850  				},
  2851  			},
  2852  		},
  2853  		{
  2854  			field: &descriptor.Field{
  2855  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2856  					Name:     proto.String("wrapped_field"),
  2857  					TypeName: proto.String(".google.protobuf.ListValue"),
  2858  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2859  				},
  2860  			},
  2861  			refs: make(refMap),
  2862  			expected: swaggerSchemaObject{
  2863  				schemaCore: schemaCore{
  2864  					Type: "array",
  2865  					Items: (*swaggerItemsObject)(&schemaCore{
  2866  						Type: "object",
  2867  					}),
  2868  				},
  2869  			},
  2870  		},
  2871  		{
  2872  			field: &descriptor.Field{
  2873  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2874  					Name:     proto.String("wrapped_field"),
  2875  					TypeName: proto.String(".google.protobuf.NullValue"),
  2876  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2877  				},
  2878  			},
  2879  			refs: make(refMap),
  2880  			expected: swaggerSchemaObject{
  2881  				schemaCore: schemaCore{
  2882  					Type: "string",
  2883  				},
  2884  			},
  2885  		},
  2886  		{
  2887  			field: &descriptor.Field{
  2888  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2889  					Name:     proto.String("message_field"),
  2890  					TypeName: proto.String(".example.Message"),
  2891  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2892  				},
  2893  			},
  2894  			refs: refMap{".example.Message": struct{}{}},
  2895  			expected: swaggerSchemaObject{
  2896  				schemaCore: schemaCore{
  2897  					Ref: "#/definitions/exampleMessage",
  2898  				},
  2899  			},
  2900  		},
  2901  		{
  2902  			field: &descriptor.Field{
  2903  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2904  					Name:     proto.String("map_field"),
  2905  					Label:    protodescriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  2906  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2907  					TypeName: proto.String(".example.Message.MapFieldEntry"),
  2908  					Options:  fieldOptions,
  2909  				},
  2910  			},
  2911  			refs: make(refMap),
  2912  			expected: swaggerSchemaObject{
  2913  				schemaCore: schemaCore{
  2914  					Type: "object",
  2915  				},
  2916  				AdditionalProperties: &swaggerSchemaObject{
  2917  					schemaCore: schemaCore{Type: "string"},
  2918  				},
  2919  				Title:       "field title",
  2920  				Description: "field description",
  2921  			},
  2922  		},
  2923  		{
  2924  			field: &descriptor.Field{
  2925  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2926  					Name:    proto.String("array_field"),
  2927  					Label:   protodescriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  2928  					Type:    protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2929  					Options: fieldOptions,
  2930  				},
  2931  			},
  2932  			refs: make(refMap),
  2933  			expected: swaggerSchemaObject{
  2934  				schemaCore: schemaCore{
  2935  					Type:  "array",
  2936  					Items: (*swaggerItemsObject)(&schemaCore{Type: "string"}),
  2937  				},
  2938  				Title:       "field title",
  2939  				Description: "field description",
  2940  			},
  2941  		},
  2942  		{
  2943  			field: &descriptor.Field{
  2944  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2945  					Name:    proto.String("primitive_field"),
  2946  					Label:   protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2947  					Type:    protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
  2948  					Options: fieldOptions,
  2949  				},
  2950  			},
  2951  			refs: make(refMap),
  2952  			expected: swaggerSchemaObject{
  2953  				schemaCore: schemaCore{
  2954  					Type:   "integer",
  2955  					Format: "int32",
  2956  				},
  2957  				Title:       "field title",
  2958  				Description: "field description",
  2959  			},
  2960  		},
  2961  		{
  2962  			field: &descriptor.Field{
  2963  				FieldDescriptorProto: &protodescriptor.FieldDescriptorProto{
  2964  					Name:     proto.String("message_field"),
  2965  					Label:    protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2966  					Type:     protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2967  					TypeName: proto.String(".example.Empty"),
  2968  					Options:  fieldOptions,
  2969  				},
  2970  			},
  2971  			refs: refMap{".example.Empty": struct{}{}},
  2972  			expected: swaggerSchemaObject{
  2973  				schemaCore: schemaCore{
  2974  					Ref: "#/definitions/exampleEmpty",
  2975  				},
  2976  				Title:       "field title",
  2977  				Description: "field description",
  2978  			},
  2979  		},
  2980  	}
  2981  
  2982  	reg := descriptor.NewRegistry()
  2983  	reg.Load(&plugin.CodeGeneratorRequest{
  2984  		ProtoFile: []*protodescriptor.FileDescriptorProto{
  2985  			{
  2986  				SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  2987  				Name:           proto.String("example.proto"),
  2988  				Package:        proto.String("example"),
  2989  				Dependency:     []string{},
  2990  				MessageType: []*protodescriptor.DescriptorProto{
  2991  					{
  2992  						Name: proto.String("Message"),
  2993  						Field: []*protodescriptor.FieldDescriptorProto{
  2994  							{
  2995  								Name: proto.String("value"),
  2996  								Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  2997  							},
  2998  						},
  2999  						NestedType: []*protodescriptor.DescriptorProto{
  3000  							{
  3001  								Name:    proto.String("MapFieldEntry"),
  3002  								Options: &protodescriptor.MessageOptions{MapEntry: proto.Bool(true)},
  3003  								Field: []*protodescriptor.FieldDescriptorProto{
  3004  									{},
  3005  									{
  3006  										Name:  proto.String("value"),
  3007  										Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3008  										Type:  protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
  3009  									},
  3010  								},
  3011  							},
  3012  						},
  3013  					},
  3014  					{
  3015  						Name: proto.String("Empty"),
  3016  					},
  3017  				},
  3018  				EnumType: []*protodescriptor.EnumDescriptorProto{
  3019  					{
  3020  						Name: proto.String("Message"),
  3021  					},
  3022  				},
  3023  				Service: []*protodescriptor.ServiceDescriptorProto{},
  3024  			},
  3025  		},
  3026  	})
  3027  
  3028  	for _, test := range tests {
  3029  		refs := make(refMap)
  3030  		actual := schemaOfField(test.field, reg, refs)
  3031  		expectedSchemaObject := test.expected
  3032  		if e, a := expectedSchemaObject, actual; !reflect.DeepEqual(a, e) {
  3033  			t.Errorf("Expected schemaOfField(%v) = %v, actual: %v", test.field, e, a)
  3034  		}
  3035  		if !reflect.DeepEqual(refs, test.refs) {
  3036  			t.Errorf("Expected schemaOfField(%v) to add refs %v, not %v", test.field, test.refs, refs)
  3037  		}
  3038  	}
  3039  }
  3040  
  3041  func TestRenderMessagesAsDefinition(t *testing.T) {
  3042  
  3043  	tests := []struct {
  3044  		descr    string
  3045  		msgDescs []*protodescriptor.DescriptorProto
  3046  		schema   map[string]swagger_options.Schema // per-message schema to add
  3047  		defs     swaggerDefinitionsObject
  3048  	}{
  3049  		{
  3050  			descr: "no swagger options",
  3051  			msgDescs: []*protodescriptor.DescriptorProto{
  3052  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3053  			},
  3054  			schema: map[string]swagger_options.Schema{},
  3055  			defs: map[string]swaggerSchemaObject{
  3056  				"Message": swaggerSchemaObject{schemaCore: schemaCore{Type: "object"}},
  3057  			},
  3058  		},
  3059  		{
  3060  			descr: "example option",
  3061  			msgDescs: []*protodescriptor.DescriptorProto{
  3062  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3063  			},
  3064  			schema: map[string]swagger_options.Schema{
  3065  				"Message": swagger_options.Schema{
  3066  					ExampleString: `{"foo":"bar"}`,
  3067  				},
  3068  			},
  3069  			defs: map[string]swaggerSchemaObject{
  3070  				"Message": swaggerSchemaObject{schemaCore: schemaCore{
  3071  					Type:    "object",
  3072  					Example: json.RawMessage(`{"foo":"bar"}`),
  3073  				}},
  3074  			},
  3075  		},
  3076  		{
  3077  			descr: "example option with something non-json",
  3078  			msgDescs: []*protodescriptor.DescriptorProto{
  3079  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3080  			},
  3081  			schema: map[string]swagger_options.Schema{
  3082  				"Message": swagger_options.Schema{
  3083  					ExampleString: `XXXX anything goes XXXX`,
  3084  				},
  3085  			},
  3086  			defs: map[string]swaggerSchemaObject{
  3087  				"Message": swaggerSchemaObject{schemaCore: schemaCore{
  3088  					Type:    "object",
  3089  					Example: json.RawMessage(`XXXX anything goes XXXX`),
  3090  				}},
  3091  			},
  3092  		},
  3093  		{
  3094  			descr: "external docs option",
  3095  			msgDescs: []*protodescriptor.DescriptorProto{
  3096  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3097  			},
  3098  			schema: map[string]swagger_options.Schema{
  3099  				"Message": swagger_options.Schema{
  3100  					ExternalDocs: &swagger_options.ExternalDocumentation{
  3101  						Description: "glorious docs",
  3102  						Url:         "https://nada",
  3103  					},
  3104  				},
  3105  			},
  3106  			defs: map[string]swaggerSchemaObject{
  3107  				"Message": swaggerSchemaObject{
  3108  					schemaCore: schemaCore{
  3109  						Type: "object",
  3110  					},
  3111  					ExternalDocs: &swaggerExternalDocumentationObject{
  3112  						Description: "glorious docs",
  3113  						URL:         "https://nada",
  3114  					},
  3115  				},
  3116  			},
  3117  		},
  3118  		{
  3119  			descr: "JSONSchema options",
  3120  			msgDescs: []*protodescriptor.DescriptorProto{
  3121  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3122  			},
  3123  			schema: map[string]swagger_options.Schema{
  3124  				"Message": swagger_options.Schema{
  3125  					JsonSchema: &swagger_options.JSONSchema{
  3126  						Title:            "title",
  3127  						Description:      "desc",
  3128  						MultipleOf:       100,
  3129  						Maximum:          101,
  3130  						ExclusiveMaximum: true,
  3131  						Minimum:          1,
  3132  						ExclusiveMinimum: true,
  3133  						MaxLength:        10,
  3134  						MinLength:        3,
  3135  						Pattern:          "[a-z]+",
  3136  						MaxItems:         20,
  3137  						MinItems:         2,
  3138  						UniqueItems:      true,
  3139  						MaxProperties:    33,
  3140  						MinProperties:    22,
  3141  						Required:         []string{"req"},
  3142  						ReadOnly:         true,
  3143  					},
  3144  				},
  3145  			},
  3146  			defs: map[string]swaggerSchemaObject{
  3147  				"Message": swaggerSchemaObject{
  3148  					schemaCore: schemaCore{
  3149  						Type: "object",
  3150  					},
  3151  					Title:            "title",
  3152  					Description:      "desc",
  3153  					MultipleOf:       100,
  3154  					Maximum:          101,
  3155  					ExclusiveMaximum: true,
  3156  					Minimum:          1,
  3157  					ExclusiveMinimum: true,
  3158  					MaxLength:        10,
  3159  					MinLength:        3,
  3160  					Pattern:          "[a-z]+",
  3161  					MaxItems:         20,
  3162  					MinItems:         2,
  3163  					UniqueItems:      true,
  3164  					MaxProperties:    33,
  3165  					MinProperties:    22,
  3166  					Required:         []string{"req"},
  3167  					ReadOnly:         true,
  3168  				},
  3169  			},
  3170  		},
  3171  	}
  3172  
  3173  	for _, test := range tests {
  3174  		t.Run(test.descr, func(t *testing.T) {
  3175  
  3176  			msgs := []*descriptor.Message{}
  3177  			for _, msgdesc := range test.msgDescs {
  3178  				msgdesc.Options = &protodescriptor.MessageOptions{}
  3179  				msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  3180  			}
  3181  
  3182  			reg := descriptor.NewRegistry()
  3183  			file := descriptor.File{
  3184  				FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  3185  					SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  3186  					Name:           proto.String("example.proto"),
  3187  					Package:        proto.String("example"),
  3188  					Dependency:     []string{},
  3189  					MessageType:    test.msgDescs,
  3190  					EnumType:       []*protodescriptor.EnumDescriptorProto{},
  3191  					Service:        []*protodescriptor.ServiceDescriptorProto{},
  3192  				},
  3193  				Messages: msgs,
  3194  			}
  3195  			reg.Load(&plugin.CodeGeneratorRequest{
  3196  				ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
  3197  			})
  3198  
  3199  			msgMap := map[string]*descriptor.Message{}
  3200  			for _, d := range test.msgDescs {
  3201  				name := d.GetName()
  3202  				msg, err := reg.LookupMsg("example", name)
  3203  				if err != nil {
  3204  					t.Fatalf("lookup message %v: %v", name, err)
  3205  				}
  3206  				msgMap[msg.FQMN()] = msg
  3207  
  3208  				if schema, ok := test.schema[name]; ok {
  3209  					err := proto.SetExtension(d.Options, swagger_options.E_Openapiv2Schema, &schema)
  3210  					if err != nil {
  3211  						t.Fatalf("SetExtension(%s, ...) returned error: %v", msg, err)
  3212  					}
  3213  				}
  3214  			}
  3215  
  3216  			refs := make(refMap)
  3217  			actual := make(swaggerDefinitionsObject)
  3218  			renderMessagesAsDefinition(msgMap, actual, reg, refs)
  3219  
  3220  			if !reflect.DeepEqual(actual, test.defs) {
  3221  				t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
  3222  			}
  3223  		})
  3224  	}
  3225  }
  3226  
  3227  func TestUpdateSwaggerDataFromComments(t *testing.T) {
  3228  
  3229  	tests := []struct {
  3230  		descr                 string
  3231  		swaggerObject         interface{}
  3232  		comments              string
  3233  		expectedError         error
  3234  		expectedSwaggerObject interface{}
  3235  		useGoTemplate         bool
  3236  	}{
  3237  		{
  3238  			descr:                 "empty comments",
  3239  			swaggerObject:         nil,
  3240  			expectedSwaggerObject: nil,
  3241  			comments:              "",
  3242  			expectedError:         nil,
  3243  		},
  3244  		{
  3245  			descr:         "set field to read only",
  3246  			swaggerObject: &swaggerSchemaObject{},
  3247  			expectedSwaggerObject: &swaggerSchemaObject{
  3248  				ReadOnly:    true,
  3249  				Description: "... Output only. ...",
  3250  			},
  3251  			comments:      "... Output only. ...",
  3252  			expectedError: nil,
  3253  		},
  3254  		{
  3255  			descr:         "set title",
  3256  			swaggerObject: &swaggerSchemaObject{},
  3257  			expectedSwaggerObject: &swaggerSchemaObject{
  3258  				Title: "Comment with no trailing dot",
  3259  			},
  3260  			comments:      "Comment with no trailing dot",
  3261  			expectedError: nil,
  3262  		},
  3263  		{
  3264  			descr:         "set description",
  3265  			swaggerObject: &swaggerSchemaObject{},
  3266  			expectedSwaggerObject: &swaggerSchemaObject{
  3267  				Description: "Comment with trailing dot.",
  3268  			},
  3269  			comments:      "Comment with trailing dot.",
  3270  			expectedError: nil,
  3271  		},
  3272  		{
  3273  			descr: "use info object",
  3274  			swaggerObject: &swaggerObject{
  3275  				Info: swaggerInfoObject{},
  3276  			},
  3277  			expectedSwaggerObject: &swaggerObject{
  3278  				Info: swaggerInfoObject{
  3279  					Description: "Comment with trailing dot.",
  3280  				},
  3281  			},
  3282  			comments:      "Comment with trailing dot.",
  3283  			expectedError: nil,
  3284  		},
  3285  		{
  3286  			descr:         "multi line comment with title",
  3287  			swaggerObject: &swaggerSchemaObject{},
  3288  			expectedSwaggerObject: &swaggerSchemaObject{
  3289  				Title:       "First line",
  3290  				Description: "Second line",
  3291  			},
  3292  			comments:      "First line\n\nSecond line",
  3293  			expectedError: nil,
  3294  		},
  3295  		{
  3296  			descr:         "multi line comment no title",
  3297  			swaggerObject: &swaggerSchemaObject{},
  3298  			expectedSwaggerObject: &swaggerSchemaObject{
  3299  				Description: "First line.\n\nSecond line",
  3300  			},
  3301  			comments:      "First line.\n\nSecond line",
  3302  			expectedError: nil,
  3303  		},
  3304  		{
  3305  			descr:         "multi line comment with summary with dot",
  3306  			swaggerObject: &swaggerOperationObject{},
  3307  			expectedSwaggerObject: &swaggerOperationObject{
  3308  				Summary:     "First line.",
  3309  				Description: "Second line",
  3310  			},
  3311  			comments:      "First line.\n\nSecond line",
  3312  			expectedError: nil,
  3313  		},
  3314  		{
  3315  			descr:         "multi line comment with summary no dot",
  3316  			swaggerObject: &swaggerOperationObject{},
  3317  			expectedSwaggerObject: &swaggerOperationObject{
  3318  				Summary:     "First line",
  3319  				Description: "Second line",
  3320  			},
  3321  			comments:      "First line\n\nSecond line",
  3322  			expectedError: nil,
  3323  		},
  3324  		{
  3325  			descr:                 "multi line comment with summary no dot",
  3326  			swaggerObject:         &schemaCore{},
  3327  			expectedSwaggerObject: &schemaCore{},
  3328  			comments:              "Any comment",
  3329  			expectedError:         errors.New("no description nor summary property"),
  3330  		},
  3331  		{
  3332  			descr:         "without use_go_template",
  3333  			swaggerObject: &swaggerSchemaObject{},
  3334  			expectedSwaggerObject: &swaggerSchemaObject{
  3335  				Title:       "First line",
  3336  				Description: "{{import \"documentation.md\"}}",
  3337  			},
  3338  			comments:      "First line\n\n{{import \"documentation.md\"}}",
  3339  			expectedError: nil,
  3340  		},
  3341  		{
  3342  			descr:         "error with use_go_template",
  3343  			swaggerObject: &swaggerSchemaObject{},
  3344  			expectedSwaggerObject: &swaggerSchemaObject{
  3345  				Title:       "First line",
  3346  				Description: "open noneexistingfile.txt: no such file or directory",
  3347  			},
  3348  			comments:      "First line\n\n{{import \"noneexistingfile.txt\"}}",
  3349  			expectedError: nil,
  3350  			useGoTemplate: true,
  3351  		},
  3352  		{
  3353  			descr:         "template with use_go_template",
  3354  			swaggerObject: &swaggerSchemaObject{},
  3355  			expectedSwaggerObject: &swaggerSchemaObject{
  3356  				Title:       "Template",
  3357  				Description: `Description "which means nothing"`,
  3358  			},
  3359  			comments:      "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3360  			expectedError: nil,
  3361  			useGoTemplate: true,
  3362  		},
  3363  	}
  3364  
  3365  	for _, test := range tests {
  3366  		t.Run(test.descr, func(t *testing.T) {
  3367  			reg := descriptor.NewRegistry()
  3368  			if test.useGoTemplate {
  3369  				reg.SetUseGoTemplate(true)
  3370  			}
  3371  			err := updateSwaggerDataFromComments(reg, test.swaggerObject, nil, test.comments, false)
  3372  			if test.expectedError == nil {
  3373  				if err != nil {
  3374  					t.Errorf("unexpected error '%v'", err)
  3375  				}
  3376  				if !reflect.DeepEqual(test.swaggerObject, test.expectedSwaggerObject) {
  3377  					t.Errorf("swaggerObject was not updated corretly, expected '%+v', got '%+v'", test.expectedSwaggerObject, test.swaggerObject)
  3378  				}
  3379  			} else {
  3380  				if err == nil {
  3381  					t.Error("expected update error not returned")
  3382  				}
  3383  				if !reflect.DeepEqual(test.swaggerObject, test.expectedSwaggerObject) {
  3384  					t.Errorf("swaggerObject was not updated corretly, expected '%+v', got '%+v'", test.expectedSwaggerObject, test.swaggerObject)
  3385  				}
  3386  				if err.Error() != test.expectedError.Error() {
  3387  					t.Errorf("expected error malformed, expected %q, got %q", test.expectedError.Error(), err.Error())
  3388  				}
  3389  			}
  3390  		})
  3391  	}
  3392  }
  3393  
  3394  func TestMessageOptionsWithGoTemplate(t *testing.T) {
  3395  	tests := []struct {
  3396  		descr         string
  3397  		msgDescs      []*protodescriptor.DescriptorProto
  3398  		schema        map[string]swagger_options.Schema // per-message schema to add
  3399  		defs          swaggerDefinitionsObject
  3400  		useGoTemplate bool
  3401  	}{
  3402  		{
  3403  			descr: "external docs option",
  3404  			msgDescs: []*protodescriptor.DescriptorProto{
  3405  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3406  			},
  3407  			schema: map[string]swagger_options.Schema{
  3408  				"Message": swagger_options.Schema{
  3409  					JsonSchema: &swagger_options.JSONSchema{
  3410  						Title:       "{{.Name}}",
  3411  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3412  					},
  3413  					ExternalDocs: &swagger_options.ExternalDocumentation{
  3414  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3415  					},
  3416  				},
  3417  			},
  3418  			defs: map[string]swaggerSchemaObject{
  3419  				"Message": swaggerSchemaObject{
  3420  					schemaCore: schemaCore{
  3421  						Type: "object",
  3422  					},
  3423  					Title:       "Message",
  3424  					Description: `Description "which means nothing"`,
  3425  					ExternalDocs: &swaggerExternalDocumentationObject{
  3426  						Description: `Description "which means nothing"`,
  3427  					},
  3428  				},
  3429  			},
  3430  			useGoTemplate: true,
  3431  		},
  3432  		{
  3433  			descr: "external docs option",
  3434  			msgDescs: []*protodescriptor.DescriptorProto{
  3435  				&protodescriptor.DescriptorProto{Name: proto.String("Message")},
  3436  			},
  3437  			schema: map[string]swagger_options.Schema{
  3438  				"Message": swagger_options.Schema{
  3439  					JsonSchema: &swagger_options.JSONSchema{
  3440  						Title:       "{{.Name}}",
  3441  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3442  					},
  3443  					ExternalDocs: &swagger_options.ExternalDocumentation{
  3444  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3445  					},
  3446  				},
  3447  			},
  3448  			defs: map[string]swaggerSchemaObject{
  3449  				"Message": swaggerSchemaObject{
  3450  					schemaCore: schemaCore{
  3451  						Type: "object",
  3452  					},
  3453  					Title:       "{{.Name}}",
  3454  					Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3455  					ExternalDocs: &swaggerExternalDocumentationObject{
  3456  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  3457  					},
  3458  				},
  3459  			},
  3460  			useGoTemplate: false,
  3461  		},
  3462  	}
  3463  
  3464  	for _, test := range tests {
  3465  		t.Run(test.descr, func(t *testing.T) {
  3466  
  3467  			msgs := []*descriptor.Message{}
  3468  			for _, msgdesc := range test.msgDescs {
  3469  				msgdesc.Options = &protodescriptor.MessageOptions{}
  3470  				msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  3471  			}
  3472  
  3473  			reg := descriptor.NewRegistry()
  3474  			reg.SetUseGoTemplate(test.useGoTemplate)
  3475  			file := descriptor.File{
  3476  				FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  3477  					SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  3478  					Name:           proto.String("example.proto"),
  3479  					Package:        proto.String("example"),
  3480  					Dependency:     []string{},
  3481  					MessageType:    test.msgDescs,
  3482  					EnumType:       []*protodescriptor.EnumDescriptorProto{},
  3483  					Service:        []*protodescriptor.ServiceDescriptorProto{},
  3484  				},
  3485  				Messages: msgs,
  3486  			}
  3487  			reg.Load(&plugin.CodeGeneratorRequest{
  3488  				ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
  3489  			})
  3490  
  3491  			msgMap := map[string]*descriptor.Message{}
  3492  			for _, d := range test.msgDescs {
  3493  				name := d.GetName()
  3494  				msg, err := reg.LookupMsg("example", name)
  3495  				if err != nil {
  3496  					t.Fatalf("lookup message %v: %v", name, err)
  3497  				}
  3498  				msgMap[msg.FQMN()] = msg
  3499  
  3500  				if schema, ok := test.schema[name]; ok {
  3501  					err := proto.SetExtension(d.Options, swagger_options.E_Openapiv2Schema, &schema)
  3502  					if err != nil {
  3503  						t.Fatalf("SetExtension(%s, ...) returned error: %v", msg, err)
  3504  					}
  3505  				}
  3506  			}
  3507  
  3508  			refs := make(refMap)
  3509  			actual := make(swaggerDefinitionsObject)
  3510  			renderMessagesAsDefinition(msgMap, actual, reg, refs)
  3511  
  3512  			if !reflect.DeepEqual(actual, test.defs) {
  3513  				t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
  3514  			}
  3515  		})
  3516  	}
  3517  }
  3518  
  3519  func TestTemplateWithoutErrorDefinition(t *testing.T) {
  3520  	msgdesc := &protodescriptor.DescriptorProto{
  3521  		Name:  proto.String("ExampleMessage"),
  3522  		Field: []*protodescriptor.FieldDescriptorProto{},
  3523  	}
  3524  	meth := &protodescriptor.MethodDescriptorProto{
  3525  		Name:       proto.String("Echo"),
  3526  		InputType:  proto.String("ExampleMessage"),
  3527  		OutputType: proto.String("ExampleMessage"),
  3528  	}
  3529  	svc := &protodescriptor.ServiceDescriptorProto{
  3530  		Name:   proto.String("ExampleService"),
  3531  		Method: []*protodescriptor.MethodDescriptorProto{meth},
  3532  	}
  3533  
  3534  	msg := &descriptor.Message{
  3535  		DescriptorProto: msgdesc,
  3536  	}
  3537  
  3538  	file := descriptor.File{
  3539  		FileDescriptorProto: &protodescriptor.FileDescriptorProto{
  3540  			SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
  3541  			Name:           proto.String("example.proto"),
  3542  			Package:        proto.String("example"),
  3543  			MessageType:    []*protodescriptor.DescriptorProto{msgdesc, msgdesc},
  3544  			Service:        []*protodescriptor.ServiceDescriptorProto{svc},
  3545  		},
  3546  		GoPkg: descriptor.GoPackage{
  3547  			Path: "example.com/path/to/example/example.pb",
  3548  			Name: "example_pb",
  3549  		},
  3550  		Messages: []*descriptor.Message{msg},
  3551  		Services: []*descriptor.Service{
  3552  			{
  3553  				ServiceDescriptorProto: svc,
  3554  				Methods: []*descriptor.Method{
  3555  					{
  3556  						MethodDescriptorProto: meth,
  3557  						RequestType:           msg,
  3558  						ResponseType:          msg,
  3559  						Bindings: []*descriptor.Binding{
  3560  							{
  3561  								HTTPMethod: "POST",
  3562  								PathTmpl: httprule.Template{
  3563  									Version:  1,
  3564  									OpCodes:  []int{0, 0},
  3565  									Template: "/v1/echo",
  3566  								},
  3567  								Body: &descriptor.Body{
  3568  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  3569  								},
  3570  							},
  3571  						},
  3572  					},
  3573  				},
  3574  			},
  3575  		},
  3576  	}
  3577  	reg := descriptor.NewRegistry()
  3578  	reg.Load(&plugin.CodeGeneratorRequest{ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto}})
  3579  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  3580  	if err != nil {
  3581  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  3582  		return
  3583  	}
  3584  
  3585  	defRsp, ok := result.Paths["/v1/echo"].Post.Responses["default"]
  3586  	if !ok {
  3587  		return
  3588  	}
  3589  
  3590  	ref := defRsp.Schema.schemaCore.Ref
  3591  	refName := strings.TrimPrefix(ref, "#/definitions/")
  3592  	if refName == "" {
  3593  		t.Fatal("created default Error response with empty reflink")
  3594  	}
  3595  
  3596  	if _, ok := result.Definitions[refName]; !ok {
  3597  		t.Errorf("default Error response with reflink '%v', but its definition was not found", refName)
  3598  	}
  3599  }
  3600  
  3601  func Test_getReservedJsonName(t *testing.T) {
  3602  	type args struct {
  3603  		fieldName                     string
  3604  		messageNameToFieldsToJSONName map[string]map[string]string
  3605  		fieldNameToType               map[string]string
  3606  	}
  3607  	tests := []struct {
  3608  		name string
  3609  		args args
  3610  		want string
  3611  	}{
  3612  		{
  3613  			"test case 1: single dot use case",
  3614  			args{
  3615  				fieldName: "abc.a_1",
  3616  				messageNameToFieldsToJSONName: map[string]map[string]string{
  3617  					"Msg": {
  3618  						"a_1": "a1JSONNAME",
  3619  						"b_1": "b1JSONNAME",
  3620  					},
  3621  				},
  3622  				fieldNameToType: map[string]string{
  3623  					"abc": "pkg1.test.Msg",
  3624  					"bcd": "pkg1.test.Msg",
  3625  				},
  3626  			},
  3627  			"a1JSONNAME",
  3628  		},
  3629  		{
  3630  			"test case 2: single dot use case with no existing field",
  3631  			args{
  3632  				fieldName: "abc.d_1",
  3633  				messageNameToFieldsToJSONName: map[string]map[string]string{
  3634  					"Msg": {
  3635  						"a_1": "a1JSONNAME",
  3636  						"b_1": "b1JSONNAME",
  3637  					},
  3638  				},
  3639  				fieldNameToType: map[string]string{
  3640  					"abc": "pkg1.test.Msg",
  3641  					"bcd": "pkg1.test.Msg",
  3642  				},
  3643  			},
  3644  			"",
  3645  		},
  3646  		{
  3647  			"test case 3: double dot use case",
  3648  			args{
  3649  				fieldName: "pkg.abc.a_1",
  3650  				messageNameToFieldsToJSONName: map[string]map[string]string{
  3651  					"Msg": {
  3652  						"a_1": "a1JSONNAME",
  3653  						"b_1": "b1JSONNAME",
  3654  					},
  3655  				},
  3656  				fieldNameToType: map[string]string{
  3657  					"abc": "pkg1.test.Msg",
  3658  					"bcd": "pkg1.test.Msg",
  3659  				},
  3660  			},
  3661  			"a1JSONNAME",
  3662  		},
  3663  		{
  3664  			"test case 4: double dot use case with a not existed field",
  3665  			args{
  3666  				fieldName: "pkg.abc.c_1",
  3667  				messageNameToFieldsToJSONName: map[string]map[string]string{
  3668  					"Msg": {
  3669  						"a_1": "a1JSONNAME",
  3670  						"b_1": "b1JSONNAME",
  3671  					},
  3672  				},
  3673  				fieldNameToType: map[string]string{
  3674  					"abc": "pkg1.test.Msg",
  3675  					"bcd": "pkg1.test.Msg",
  3676  				},
  3677  			},
  3678  			"",
  3679  		},
  3680  	}
  3681  	for _, tt := range tests {
  3682  		t.Run(tt.name, func(t *testing.T) {
  3683  			if got := getReservedJSONName(tt.args.fieldName, tt.args.messageNameToFieldsToJSONName, tt.args.fieldNameToType); got != tt.want {
  3684  				t.Errorf("getReservedJSONName() = %v, want %v", got, tt.want)
  3685  			}
  3686  		})
  3687  	}
  3688  }
  3689  

View as plain text