reg/vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor/services_test.go
Jess Frazelle 843aebf2c1
update deps
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-07-14 11:48:41 -04:00

1211 lines
28 KiB
Go

package descriptor
import (
"reflect"
"testing"
"github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
)
func compilePath(t *testing.T, path string) httprule.Template {
parsed, err := httprule.Parse(path)
if err != nil {
t.Fatalf("httprule.Parse(%q) failed with %v; want success", path, err)
}
return parsed.Compile()
}
func testExtractServices(t *testing.T, input []*descriptor.FileDescriptorProto, target string, wantSvcs []*Service) {
reg := NewRegistry()
for _, file := range input {
reg.loadFile(file)
}
err := reg.loadServices(reg.files[target])
if err != nil {
t.Errorf("loadServices(%q) failed with %v; want success; files=%v", target, err, input)
}
file := reg.files[target]
svcs := file.Services
var i int
for i = 0; i < len(svcs) && i < len(wantSvcs); i++ {
svc, wantSvc := svcs[i], wantSvcs[i]
if got, want := svc.ServiceDescriptorProto, wantSvc.ServiceDescriptorProto; !proto.Equal(got, want) {
t.Errorf("svcs[%d].ServiceDescriptorProto = %v; want %v; input = %v", i, got, want, input)
continue
}
var j int
for j = 0; j < len(svc.Methods) && j < len(wantSvc.Methods); j++ {
meth, wantMeth := svc.Methods[j], wantSvc.Methods[j]
if got, want := meth.MethodDescriptorProto, wantMeth.MethodDescriptorProto; !proto.Equal(got, want) {
t.Errorf("svcs[%d].Methods[%d].MethodDescriptorProto = %v; want %v; input = %v", i, j, got, want, input)
continue
}
if got, want := meth.RequestType, wantMeth.RequestType; got.FQMN() != want.FQMN() {
t.Errorf("svcs[%d].Methods[%d].RequestType = %s; want %s; input = %v", i, j, got.FQMN(), want.FQMN(), input)
}
if got, want := meth.ResponseType, wantMeth.ResponseType; got.FQMN() != want.FQMN() {
t.Errorf("svcs[%d].Methods[%d].ResponseType = %s; want %s; input = %v", i, j, got.FQMN(), want.FQMN(), input)
}
var k int
for k = 0; k < len(meth.Bindings) && k < len(wantMeth.Bindings); k++ {
binding, wantBinding := meth.Bindings[k], wantMeth.Bindings[k]
if got, want := binding.Index, wantBinding.Index; got != want {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Index = %d; want %d; input = %v", i, j, k, got, want, input)
}
if got, want := binding.PathTmpl, wantBinding.PathTmpl; !reflect.DeepEqual(got, want) {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathTmpl = %#v; want %#v; input = %v", i, j, k, got, want, input)
}
if got, want := binding.HTTPMethod, wantBinding.HTTPMethod; got != want {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].HTTPMethod = %q; want %q; input = %v", i, j, k, got, want, input)
}
var l int
for l = 0; l < len(binding.PathParams) && l < len(wantBinding.PathParams); l++ {
param, wantParam := binding.PathParams[l], wantBinding.PathParams[l]
if got, want := param.FieldPath.String(), wantParam.FieldPath.String(); got != want {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d].FieldPath.String() = %q; want %q; input = %v", i, j, k, l, got, want, input)
continue
}
for m := 0; m < len(param.FieldPath) && m < len(wantParam.FieldPath); m++ {
field, wantField := param.FieldPath[m].Target, wantParam.FieldPath[m].Target
if got, want := field.FieldDescriptorProto, wantField.FieldDescriptorProto; !proto.Equal(got, want) {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d].FieldPath[%d].Target.FieldDescriptorProto = %v; want %v; input = %v", i, j, k, l, m, got, want, input)
}
}
}
for ; l < len(binding.PathParams); l++ {
got := binding.PathParams[l].FieldPath.String()
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d] = %q; want it to be missing; input = %v", i, j, k, l, got, input)
}
for ; l < len(wantBinding.PathParams); l++ {
want := wantBinding.PathParams[l].FieldPath.String()
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d] missing; want %q; input = %v", i, j, k, l, want, input)
}
if got, want := (binding.Body != nil), (wantBinding.Body != nil); got != want {
if got {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Body = %q; want it to be missing; input = %v", i, j, k, binding.Body.FieldPath.String(), input)
} else {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Body missing; want %q; input = %v", i, j, k, wantBinding.Body.FieldPath.String(), input)
}
} else if binding.Body != nil {
if got, want := binding.Body.FieldPath.String(), wantBinding.Body.FieldPath.String(); got != want {
t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Body = %q; want %q; input = %v", i, j, k, got, want, input)
}
}
}
for ; k < len(meth.Bindings); k++ {
got := meth.Bindings[k]
t.Errorf("svcs[%d].Methods[%d].Bindings[%d] = %q; want it to be missing; input = %v", i, j, k, got, input)
}
for ; k < len(wantMeth.Bindings); k++ {
want := wantMeth.Bindings[k]
t.Errorf("svcs[%d].Methods[%d].Bindings[%d] missing; want %q; input = %v", i, j, k, want, input)
}
}
for ; j < len(svc.Methods); j++ {
got := svc.Methods[j].MethodDescriptorProto
t.Errorf("svcs[%d].Methods[%d] = %v; want it to be missing; input = %v", i, j, got, input)
}
for ; j < len(wantSvc.Methods); j++ {
want := wantSvc.Methods[j].MethodDescriptorProto
t.Errorf("svcs[%d].Methods[%d] missing; want %v; input = %v", i, j, want, input)
}
}
for ; i < len(svcs); i++ {
got := svcs[i].ServiceDescriptorProto
t.Errorf("svcs[%d] = %v; want it to be missing; input = %v", i, got, input)
}
for ; i < len(wantSvcs); i++ {
want := wantSvcs[i].ServiceDescriptorProto
t.Errorf("svcs[%d] missing; want %v; input = %v", i, want, input)
}
}
func crossLinkFixture(f *File) *File {
for _, m := range f.Messages {
m.File = f
for _, f := range m.Fields {
f.Message = m
}
}
for _, svc := range f.Services {
svc.File = f
for _, m := range svc.Methods {
m.Service = svc
for _, b := range m.Bindings {
b.Method = m
for _, param := range b.PathParams {
param.Method = m
}
}
}
}
return f
}
func TestExtractServicesSimple(t *testing.T) {
src := `
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/echo"
body: "*"
>
>
>
>
`
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
msg := &Message{
DescriptorProto: fd.MessageType[0],
Fields: []*Field{
{
FieldDescriptorProto: fd.MessageType[0].Field[0],
},
},
}
file := &File{
FileDescriptorProto: &fd,
GoPkg: GoPackage{
Path: "path/to/example.pb",
Name: "example_pb",
},
Messages: []*Message{msg},
Services: []*Service{
{
ServiceDescriptorProto: fd.Service[0],
Methods: []*Method{
{
MethodDescriptorProto: fd.Service[0].Method[0],
RequestType: msg,
ResponseType: msg,
Bindings: []*Binding{
{
PathTmpl: compilePath(t, "/v1/example/echo"),
HTTPMethod: "POST",
Body: &Body{FieldPath: nil},
},
},
},
},
},
},
}
crossLinkFixture(file)
testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
}
func TestExtractServicesCrossPackage(t *testing.T) {
srcs := []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "ToString"
input_type: ".another.example.BoolMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/to_s"
body: "*"
>
>
>
>
`, `
name: "path/to/another/example.proto",
package: "another.example"
message_type <
name: "BoolMessage"
field <
name: "bool"
number: 1
label: LABEL_OPTIONAL
type: TYPE_BOOL
>
>
`,
}
var fds []*descriptor.FileDescriptorProto
for _, src := range srcs {
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
fds = append(fds, &fd)
}
stringMsg := &Message{
DescriptorProto: fds[0].MessageType[0],
Fields: []*Field{
{
FieldDescriptorProto: fds[0].MessageType[0].Field[0],
},
},
}
boolMsg := &Message{
DescriptorProto: fds[1].MessageType[0],
Fields: []*Field{
{
FieldDescriptorProto: fds[1].MessageType[0].Field[0],
},
},
}
files := []*File{
{
FileDescriptorProto: fds[0],
GoPkg: GoPackage{
Path: "path/to/example.pb",
Name: "example_pb",
},
Messages: []*Message{stringMsg},
Services: []*Service{
{
ServiceDescriptorProto: fds[0].Service[0],
Methods: []*Method{
{
MethodDescriptorProto: fds[0].Service[0].Method[0],
RequestType: boolMsg,
ResponseType: stringMsg,
Bindings: []*Binding{
{
PathTmpl: compilePath(t, "/v1/example/to_s"),
HTTPMethod: "POST",
Body: &Body{FieldPath: nil},
},
},
},
},
},
},
},
{
FileDescriptorProto: fds[1],
GoPkg: GoPackage{
Path: "path/to/another/example.pb",
Name: "example_pb",
},
Messages: []*Message{boolMsg},
},
}
for _, file := range files {
crossLinkFixture(file)
}
testExtractServices(t, fds, "path/to/example.proto", files[0].Services)
}
func TestExtractServicesWithBodyPath(t *testing.T) {
src := `
name: "path/to/example.proto",
package: "example"
message_type <
name: "OuterMessage"
nested_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
field <
name: "nested"
number: 1
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: "StringMessage"
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "OuterMessage"
output_type: "OuterMessage"
options <
[google.api.http] <
post: "/v1/example/echo"
body: "nested"
>
>
>
>
`
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
msg := &Message{
DescriptorProto: fd.MessageType[0],
Fields: []*Field{
{
FieldDescriptorProto: fd.MessageType[0].Field[0],
},
},
}
file := &File{
FileDescriptorProto: &fd,
GoPkg: GoPackage{
Path: "path/to/example.pb",
Name: "example_pb",
},
Messages: []*Message{msg},
Services: []*Service{
{
ServiceDescriptorProto: fd.Service[0],
Methods: []*Method{
{
MethodDescriptorProto: fd.Service[0].Method[0],
RequestType: msg,
ResponseType: msg,
Bindings: []*Binding{
{
PathTmpl: compilePath(t, "/v1/example/echo"),
HTTPMethod: "POST",
Body: &Body{
FieldPath: FieldPath{
{
Name: "nested",
Target: msg.Fields[0],
},
},
},
},
},
},
},
},
},
}
crossLinkFixture(file)
testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
}
func TestExtractServicesWithPathParam(t *testing.T) {
src := `
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
get: "/v1/example/echo/{string=*}"
>
>
>
>
`
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
msg := &Message{
DescriptorProto: fd.MessageType[0],
Fields: []*Field{
{
FieldDescriptorProto: fd.MessageType[0].Field[0],
},
},
}
file := &File{
FileDescriptorProto: &fd,
GoPkg: GoPackage{
Path: "path/to/example.pb",
Name: "example_pb",
},
Messages: []*Message{msg},
Services: []*Service{
{
ServiceDescriptorProto: fd.Service[0],
Methods: []*Method{
{
MethodDescriptorProto: fd.Service[0].Method[0],
RequestType: msg,
ResponseType: msg,
Bindings: []*Binding{
{
PathTmpl: compilePath(t, "/v1/example/echo/{string=*}"),
HTTPMethod: "GET",
PathParams: []Parameter{
{
FieldPath: FieldPath{
{
Name: "string",
Target: msg.Fields[0],
},
},
Target: msg.Fields[0],
},
},
},
},
},
},
},
},
}
crossLinkFixture(file)
testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
}
func TestExtractServicesWithAdditionalBinding(t *testing.T) {
src := `
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/echo"
body: "*"
additional_bindings <
get: "/v1/example/echo/{string}"
>
additional_bindings <
post: "/v2/example/echo"
body: "string"
>
>
>
>
>
`
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
msg := &Message{
DescriptorProto: fd.MessageType[0],
Fields: []*Field{
{
FieldDescriptorProto: fd.MessageType[0].Field[0],
},
},
}
file := &File{
FileDescriptorProto: &fd,
GoPkg: GoPackage{
Path: "path/to/example.pb",
Name: "example_pb",
},
Messages: []*Message{msg},
Services: []*Service{
{
ServiceDescriptorProto: fd.Service[0],
Methods: []*Method{
{
MethodDescriptorProto: fd.Service[0].Method[0],
RequestType: msg,
ResponseType: msg,
Bindings: []*Binding{
{
Index: 0,
PathTmpl: compilePath(t, "/v1/example/echo"),
HTTPMethod: "POST",
Body: &Body{FieldPath: nil},
},
{
Index: 1,
PathTmpl: compilePath(t, "/v1/example/echo/{string}"),
HTTPMethod: "GET",
PathParams: []Parameter{
{
FieldPath: FieldPath{
{
Name: "string",
Target: msg.Fields[0],
},
},
Target: msg.Fields[0],
},
},
Body: nil,
},
{
Index: 2,
PathTmpl: compilePath(t, "/v2/example/echo"),
HTTPMethod: "POST",
Body: &Body{
FieldPath: FieldPath{
FieldPathComponent{
Name: "string",
Target: msg.Fields[0],
},
},
},
},
},
},
},
},
},
}
crossLinkFixture(file)
testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
}
func TestExtractServicesWithError(t *testing.T) {
for _, spec := range []struct {
target string
srcs []string
}{
{
target: "path/to/example.proto",
srcs: []string{
// message not found
`
name: "path/to/example.proto",
package: "example"
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/echo"
body: "*"
>
>
>
>
`,
},
},
// body field path not resolved
{
target: "path/to/example.proto",
srcs: []string{`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/echo"
body: "bool"
>
>
>
>`,
},
},
// param field path not resolved
{
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/echo/{bool=*}"
>
>
>
>
`,
},
},
// non aggregate type on field path
{
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "OuterMessage"
field <
name: "mid"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
field <
name: "bool"
number: 2
label: LABEL_OPTIONAL
type: TYPE_BOOL
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "OuterMessage"
output_type: "OuterMessage"
options <
[google.api.http] <
post: "/v1/example/echo/{mid.bool=*}"
>
>
>
>
`,
},
},
// path param in client streaming
{
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
post: "/v1/example/echo/{bool=*}"
>
>
client_streaming: true
>
>
`,
},
},
// body for GET
{
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
get: "/v1/example/echo"
body: "string"
>
>
>
>
`,
},
},
// body for DELETE
{
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "RemoveResource"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
delete: "/v1/example/resource"
body: "string"
>
>
>
>
`,
},
},
// no pattern specified
{
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
service <
name: "ExampleService"
method <
name: "RemoveResource"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
body: "string"
>
>
>
>
`,
},
},
// unsupported path parameter type
{
target: "path/to/example.proto",
srcs: []string{`
name: "path/to/example.proto",
package: "example"
message_type <
name: "OuterMessage"
nested_type <
name: "StringMessage"
field <
name: "value"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: "StringMessage"
>
>
service <
name: "ExampleService"
method <
name: "Echo"
input_type: "OuterMessage"
output_type: "OuterMessage"
options <
[google.api.http] <
get: "/v1/example/echo/{string=*}"
>
>
>
>
`,
},
},
} {
reg := NewRegistry()
var fds []*descriptor.FileDescriptorProto
for _, src := range spec.srcs {
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
reg.loadFile(&fd)
fds = append(fds, &fd)
}
err := reg.loadServices(reg.files[spec.target])
if err == nil {
t.Errorf("loadServices(%q) succeeded; want an error; files=%v", spec.target, spec.srcs)
}
t.Log(err)
}
}
func TestResolveFieldPath(t *testing.T) {
for _, spec := range []struct {
src string
path string
wantErr bool
}{
{
src: `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'string'
type: TYPE_STRING
label: LABEL_OPTIONAL
number: 1
>
>
`,
path: "string",
wantErr: false,
},
// no such field
{
src: `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'string'
type: TYPE_STRING
label: LABEL_OPTIONAL
number: 1
>
>
`,
path: "something_else",
wantErr: true,
},
// repeated field
{
src: `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'string'
type: TYPE_STRING
label: LABEL_REPEATED
number: 1
>
>
`,
path: "string",
wantErr: true,
},
// nested field
{
src: `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'nested'
type: TYPE_MESSAGE
type_name: 'AnotherMessage'
label: LABEL_OPTIONAL
number: 1
>
field <
name: 'terminal'
type: TYPE_BOOL
label: LABEL_OPTIONAL
number: 2
>
>
message_type <
name: 'AnotherMessage'
field <
name: 'nested2'
type: TYPE_MESSAGE
type_name: 'ExampleMessage'
label: LABEL_OPTIONAL
number: 1
>
>
`,
path: "nested.nested2.nested.nested2.nested.nested2.terminal",
wantErr: false,
},
// non aggregate field on the path
{
src: `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'nested'
type: TYPE_MESSAGE
type_name: 'AnotherMessage'
label: LABEL_OPTIONAL
number: 1
>
field <
name: 'terminal'
type: TYPE_BOOL
label: LABEL_OPTIONAL
number: 2
>
>
message_type <
name: 'AnotherMessage'
field <
name: 'nested2'
type: TYPE_MESSAGE
type_name: 'ExampleMessage'
label: LABEL_OPTIONAL
number: 1
>
>
`,
path: "nested.terminal.nested2",
wantErr: true,
},
// repeated field
{
src: `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'nested'
type: TYPE_MESSAGE
type_name: 'AnotherMessage'
label: LABEL_OPTIONAL
number: 1
>
field <
name: 'terminal'
type: TYPE_BOOL
label: LABEL_OPTIONAL
number: 2
>
>
message_type <
name: 'AnotherMessage'
field <
name: 'nested2'
type: TYPE_MESSAGE
type_name: 'ExampleMessage'
label: LABEL_REPEATED
number: 1
>
>
`,
path: "nested.nested2.terminal",
wantErr: true,
},
} {
var file descriptor.FileDescriptorProto
if err := proto.UnmarshalText(spec.src, &file); err != nil {
t.Fatalf("proto.Unmarshal(%s) failed with %v; want success", spec.src, err)
}
reg := NewRegistry()
reg.loadFile(&file)
f, err := reg.LookupFile(file.GetName())
if err != nil {
t.Fatalf("reg.LookupFile(%q) failed with %v; want success; on file=%s", file.GetName(), err, spec.src)
}
_, err = reg.resolveFieldPath(f.Messages[0], spec.path)
if got, want := err != nil, spec.wantErr; got != want {
if want {
t.Errorf("reg.resolveFiledPath(%q, %q) succeeded; want an error", f.Messages[0].GetName(), spec.path)
continue
}
t.Errorf("reg.resolveFiledPath(%q, %q) failed with %v; want success", f.Messages[0].GetName(), spec.path, err)
}
}
}
func TestExtractServicesWithDeleteBody(t *testing.T) {
for _, spec := range []struct {
allowDeleteBody bool
expectErr bool
target string
srcs []string
}{
// body for DELETE, but registry configured to allow it
{
allowDeleteBody: true,
expectErr: false,
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "RemoveResource"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
delete: "/v1/example/resource"
body: "string"
>
>
>
>
`,
},
},
// body for DELETE, registry configured not to allow it
{
allowDeleteBody: false,
expectErr: true,
target: "path/to/example.proto",
srcs: []string{
`
name: "path/to/example.proto",
package: "example"
message_type <
name: "StringMessage"
field <
name: "string"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
>
>
service <
name: "ExampleService"
method <
name: "RemoveResource"
input_type: "StringMessage"
output_type: "StringMessage"
options <
[google.api.http] <
delete: "/v1/example/resource"
body: "string"
>
>
>
>
`,
},
},
} {
reg := NewRegistry()
reg.SetAllowDeleteBody(spec.allowDeleteBody)
var fds []*descriptor.FileDescriptorProto
for _, src := range spec.srcs {
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
reg.loadFile(&fd)
fds = append(fds, &fd)
}
err := reg.loadServices(reg.files[spec.target])
if spec.expectErr && err == nil {
t.Errorf("loadServices(%q) succeeded; want an error; allowDeleteBody=%v, files=%v", spec.target, spec.allowDeleteBody, spec.srcs)
}
if !spec.expectErr && err != nil {
t.Errorf("loadServices(%q) failed; do not want an error; allowDeleteBody=%v, files=%v", spec.target, spec.allowDeleteBody, spec.srcs)
}
t.Log(err)
}
}