1 package descriptor
2
3 import (
4 "reflect"
5 "testing"
6
7 "github.com/golang/protobuf/proto"
8 descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
9 "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
10 )
11
12 func compilePath(t *testing.T, path string) httprule.Template {
13 parsed, err := httprule.Parse(path)
14 if err != nil {
15 t.Fatalf("httprule.Parse(%q) failed with %v; want success", path, err)
16 }
17 return parsed.Compile()
18 }
19
20 func testExtractServices(t *testing.T, input []*descriptor.FileDescriptorProto, target string, wantSvcs []*Service) {
21 testExtractServicesWithRegistry(t, NewRegistry(), input, target, wantSvcs)
22 }
23
24 func testExtractServicesWithRegistry(t *testing.T, reg *Registry, input []*descriptor.FileDescriptorProto, target string, wantSvcs []*Service) {
25 for _, file := range input {
26 reg.loadFile(file)
27 }
28 err := reg.loadServices(reg.files[target])
29 if err != nil {
30 t.Errorf("loadServices(%q) failed with %v; want success; files=%v", target, err, input)
31 }
32
33 file := reg.files[target]
34 svcs := file.Services
35 var i int
36 for i = 0; i < len(svcs) && i < len(wantSvcs); i++ {
37 svc, wantSvc := svcs[i], wantSvcs[i]
38 if got, want := svc.ServiceDescriptorProto, wantSvc.ServiceDescriptorProto; !proto.Equal(got, want) {
39 t.Errorf("svcs[%d].ServiceDescriptorProto = %v; want %v; input = %v", i, got, want, input)
40 continue
41 }
42 var j int
43 for j = 0; j < len(svc.Methods) && j < len(wantSvc.Methods); j++ {
44 meth, wantMeth := svc.Methods[j], wantSvc.Methods[j]
45 if got, want := meth.MethodDescriptorProto, wantMeth.MethodDescriptorProto; !proto.Equal(got, want) {
46 t.Errorf("svcs[%d].Methods[%d].MethodDescriptorProto = %v; want %v; input = %v", i, j, got, want, input)
47 continue
48 }
49 if got, want := meth.RequestType, wantMeth.RequestType; got.FQMN() != want.FQMN() {
50 t.Errorf("svcs[%d].Methods[%d].RequestType = %s; want %s; input = %v", i, j, got.FQMN(), want.FQMN(), input)
51 }
52 if got, want := meth.ResponseType, wantMeth.ResponseType; got.FQMN() != want.FQMN() {
53 t.Errorf("svcs[%d].Methods[%d].ResponseType = %s; want %s; input = %v", i, j, got.FQMN(), want.FQMN(), input)
54 }
55 var k int
56 for k = 0; k < len(meth.Bindings) && k < len(wantMeth.Bindings); k++ {
57 binding, wantBinding := meth.Bindings[k], wantMeth.Bindings[k]
58 if got, want := binding.Index, wantBinding.Index; got != want {
59 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Index = %d; want %d; input = %v", i, j, k, got, want, input)
60 }
61 if got, want := binding.PathTmpl, wantBinding.PathTmpl; !reflect.DeepEqual(got, want) {
62 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathTmpl = %#v; want %#v; input = %v", i, j, k, got, want, input)
63 }
64 if got, want := binding.HTTPMethod, wantBinding.HTTPMethod; got != want {
65 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].HTTPMethod = %q; want %q; input = %v", i, j, k, got, want, input)
66 }
67
68 var l int
69 for l = 0; l < len(binding.PathParams) && l < len(wantBinding.PathParams); l++ {
70 param, wantParam := binding.PathParams[l], wantBinding.PathParams[l]
71 if got, want := param.FieldPath.String(), wantParam.FieldPath.String(); got != want {
72 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d].FieldPath.String() = %q; want %q; input = %v", i, j, k, l, got, want, input)
73 continue
74 }
75 for m := 0; m < len(param.FieldPath) && m < len(wantParam.FieldPath); m++ {
76 field, wantField := param.FieldPath[m].Target, wantParam.FieldPath[m].Target
77 if got, want := field.FieldDescriptorProto, wantField.FieldDescriptorProto; !proto.Equal(got, want) {
78 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)
79 }
80 }
81 }
82 for ; l < len(binding.PathParams); l++ {
83 got := binding.PathParams[l].FieldPath.String()
84 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d] = %q; want it to be missing; input = %v", i, j, k, l, got, input)
85 }
86 for ; l < len(wantBinding.PathParams); l++ {
87 want := wantBinding.PathParams[l].FieldPath.String()
88 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].PathParams[%d] missing; want %q; input = %v", i, j, k, l, want, input)
89 }
90
91 if got, want := (binding.Body != nil), (wantBinding.Body != nil); got != want {
92 if got {
93 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)
94 } else {
95 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Body missing; want %q; input = %v", i, j, k, wantBinding.Body.FieldPath.String(), input)
96 }
97 } else if binding.Body != nil {
98 if got, want := binding.Body.FieldPath.String(), wantBinding.Body.FieldPath.String(); got != want {
99 t.Errorf("svcs[%d].Methods[%d].Bindings[%d].Body = %q; want %q; input = %v", i, j, k, got, want, input)
100 }
101 }
102 }
103 for ; k < len(meth.Bindings); k++ {
104 got := meth.Bindings[k]
105 t.Errorf("svcs[%d].Methods[%d].Bindings[%d] = %v; want it to be missing; input = %v", i, j, k, got, input)
106 }
107 for ; k < len(wantMeth.Bindings); k++ {
108 want := wantMeth.Bindings[k]
109 t.Errorf("svcs[%d].Methods[%d].Bindings[%d] missing; want %v; input = %v", i, j, k, want, input)
110 }
111 }
112 for ; j < len(svc.Methods); j++ {
113 got := svc.Methods[j].MethodDescriptorProto
114 t.Errorf("svcs[%d].Methods[%d] = %v; want it to be missing; input = %v", i, j, got, input)
115 }
116 for ; j < len(wantSvc.Methods); j++ {
117 want := wantSvc.Methods[j].MethodDescriptorProto
118 t.Errorf("svcs[%d].Methods[%d] missing; want %v; input = %v", i, j, want, input)
119 }
120 }
121 for ; i < len(svcs); i++ {
122 got := svcs[i].ServiceDescriptorProto
123 t.Errorf("svcs[%d] = %v; want it to be missing; input = %v", i, got, input)
124 }
125 for ; i < len(wantSvcs); i++ {
126 want := wantSvcs[i].ServiceDescriptorProto
127 t.Errorf("svcs[%d] missing; want %v; input = %v", i, want, input)
128 }
129 }
130
131 func crossLinkFixture(f *File) *File {
132 for _, m := range f.Messages {
133 m.File = f
134 for _, f := range m.Fields {
135 f.Message = m
136 }
137 }
138 for _, svc := range f.Services {
139 svc.File = f
140 for _, m := range svc.Methods {
141 m.Service = svc
142 for _, b := range m.Bindings {
143 b.Method = m
144 for _, param := range b.PathParams {
145 param.Method = m
146 }
147 }
148 }
149 }
150 return f
151 }
152
153 func TestExtractServicesSimple(t *testing.T) {
154 src := `
155 name: "path/to/example.proto",
156 package: "example"
157 message_type <
158 name: "StringMessage"
159 field <
160 name: "string"
161 number: 1
162 label: LABEL_OPTIONAL
163 type: TYPE_STRING
164 >
165 >
166 service <
167 name: "ExampleService"
168 method <
169 name: "Echo"
170 input_type: "StringMessage"
171 output_type: "StringMessage"
172 options <
173 [google.api.http] <
174 post: "/v1/example/echo"
175 body: "*"
176 >
177 >
178 >
179 >
180 `
181 var fd descriptor.FileDescriptorProto
182 if err := proto.UnmarshalText(src, &fd); err != nil {
183 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
184 }
185 msg := &Message{
186 DescriptorProto: fd.MessageType[0],
187 Fields: []*Field{
188 {
189 FieldDescriptorProto: fd.MessageType[0].Field[0],
190 },
191 },
192 }
193 file := &File{
194 FileDescriptorProto: &fd,
195 GoPkg: GoPackage{
196 Path: "path/to/example.pb",
197 Name: "example_pb",
198 },
199 Messages: []*Message{msg},
200 Services: []*Service{
201 {
202 ServiceDescriptorProto: fd.Service[0],
203 Methods: []*Method{
204 {
205 MethodDescriptorProto: fd.Service[0].Method[0],
206 RequestType: msg,
207 ResponseType: msg,
208 Bindings: []*Binding{
209 {
210 PathTmpl: compilePath(t, "/v1/example/echo"),
211 HTTPMethod: "POST",
212 Body: &Body{FieldPath: nil},
213 },
214 },
215 },
216 },
217 },
218 },
219 }
220
221 crossLinkFixture(file)
222 testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
223 }
224
225 func TestExtractServicesWithoutAnnotation(t *testing.T) {
226 src := `
227 name: "path/to/example.proto",
228 package: "example"
229 message_type <
230 name: "StringMessage"
231 field <
232 name: "string"
233 number: 1
234 label: LABEL_OPTIONAL
235 type: TYPE_STRING
236 >
237 >
238 service <
239 name: "ExampleService"
240 method <
241 name: "Echo"
242 input_type: "StringMessage"
243 output_type: "StringMessage"
244 >
245 >
246 `
247 var fd descriptor.FileDescriptorProto
248 if err := proto.UnmarshalText(src, &fd); err != nil {
249 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
250 }
251 msg := &Message{
252 DescriptorProto: fd.MessageType[0],
253 Fields: []*Field{
254 {
255 FieldDescriptorProto: fd.MessageType[0].Field[0],
256 },
257 },
258 }
259 file := &File{
260 FileDescriptorProto: &fd,
261 GoPkg: GoPackage{
262 Path: "path/to/example.pb",
263 Name: "example_pb",
264 },
265 Messages: []*Message{msg},
266 Services: []*Service{
267 {
268 ServiceDescriptorProto: fd.Service[0],
269 Methods: []*Method{
270 {
271 MethodDescriptorProto: fd.Service[0].Method[0],
272 RequestType: msg,
273 ResponseType: msg,
274 },
275 },
276 },
277 },
278 }
279
280 crossLinkFixture(file)
281 testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
282 }
283
284 func TestExtractServicesGenerateUnboundMethods(t *testing.T) {
285 src := `
286 name: "path/to/example.proto",
287 package: "example"
288 message_type <
289 name: "StringMessage"
290 field <
291 name: "string"
292 number: 1
293 label: LABEL_OPTIONAL
294 type: TYPE_STRING
295 >
296 >
297 service <
298 name: "ExampleService"
299 method <
300 name: "Echo"
301 input_type: "StringMessage"
302 output_type: "StringMessage"
303 >
304 >
305 `
306 var fd descriptor.FileDescriptorProto
307 if err := proto.UnmarshalText(src, &fd); err != nil {
308 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
309 }
310 msg := &Message{
311 DescriptorProto: fd.MessageType[0],
312 Fields: []*Field{
313 {
314 FieldDescriptorProto: fd.MessageType[0].Field[0],
315 },
316 },
317 }
318 file := &File{
319 FileDescriptorProto: &fd,
320 GoPkg: GoPackage{
321 Path: "path/to/example.pb",
322 Name: "example_pb",
323 },
324 Messages: []*Message{msg},
325 Services: []*Service{
326 {
327 ServiceDescriptorProto: fd.Service[0],
328 Methods: []*Method{
329 {
330 MethodDescriptorProto: fd.Service[0].Method[0],
331 RequestType: msg,
332 ResponseType: msg,
333 Bindings: []*Binding{
334 {
335 PathTmpl: compilePath(t, "/example.ExampleService/Echo"),
336 HTTPMethod: "POST",
337 Body: &Body{FieldPath: nil},
338 },
339 },
340 },
341 },
342 },
343 },
344 }
345
346 crossLinkFixture(file)
347 reg := NewRegistry()
348 reg.SetGenerateUnboundMethods(true)
349 testExtractServicesWithRegistry(t, reg, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
350 }
351
352 func TestExtractServicesCrossPackage(t *testing.T) {
353 srcs := []string{
354 `
355 name: "path/to/example.proto",
356 package: "example"
357 message_type <
358 name: "StringMessage"
359 field <
360 name: "string"
361 number: 1
362 label: LABEL_OPTIONAL
363 type: TYPE_STRING
364 >
365 >
366 service <
367 name: "ExampleService"
368 method <
369 name: "ToString"
370 input_type: ".another.example.BoolMessage"
371 output_type: "StringMessage"
372 options <
373 [google.api.http] <
374 post: "/v1/example/to_s"
375 body: "*"
376 >
377 >
378 >
379 >
380 `, `
381 name: "path/to/another/example.proto",
382 package: "another.example"
383 message_type <
384 name: "BoolMessage"
385 field <
386 name: "bool"
387 number: 1
388 label: LABEL_OPTIONAL
389 type: TYPE_BOOL
390 >
391 >
392 `,
393 }
394 var fds []*descriptor.FileDescriptorProto
395 for _, src := range srcs {
396 var fd descriptor.FileDescriptorProto
397 if err := proto.UnmarshalText(src, &fd); err != nil {
398 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
399 }
400 fds = append(fds, &fd)
401 }
402 stringMsg := &Message{
403 DescriptorProto: fds[0].MessageType[0],
404 Fields: []*Field{
405 {
406 FieldDescriptorProto: fds[0].MessageType[0].Field[0],
407 },
408 },
409 }
410 boolMsg := &Message{
411 DescriptorProto: fds[1].MessageType[0],
412 Fields: []*Field{
413 {
414 FieldDescriptorProto: fds[1].MessageType[0].Field[0],
415 },
416 },
417 }
418 files := []*File{
419 {
420 FileDescriptorProto: fds[0],
421 GoPkg: GoPackage{
422 Path: "path/to/example.pb",
423 Name: "example_pb",
424 },
425 Messages: []*Message{stringMsg},
426 Services: []*Service{
427 {
428 ServiceDescriptorProto: fds[0].Service[0],
429 Methods: []*Method{
430 {
431 MethodDescriptorProto: fds[0].Service[0].Method[0],
432 RequestType: boolMsg,
433 ResponseType: stringMsg,
434 Bindings: []*Binding{
435 {
436 PathTmpl: compilePath(t, "/v1/example/to_s"),
437 HTTPMethod: "POST",
438 Body: &Body{FieldPath: nil},
439 },
440 },
441 },
442 },
443 },
444 },
445 },
446 {
447 FileDescriptorProto: fds[1],
448 GoPkg: GoPackage{
449 Path: "path/to/another/example.pb",
450 Name: "example_pb",
451 },
452 Messages: []*Message{boolMsg},
453 },
454 }
455
456 for _, file := range files {
457 crossLinkFixture(file)
458 }
459 testExtractServices(t, fds, "path/to/example.proto", files[0].Services)
460 }
461
462 func TestExtractServicesWithBodyPath(t *testing.T) {
463 src := `
464 name: "path/to/example.proto",
465 package: "example"
466 message_type <
467 name: "OuterMessage"
468 nested_type <
469 name: "StringMessage"
470 field <
471 name: "string"
472 number: 1
473 label: LABEL_OPTIONAL
474 type: TYPE_STRING
475 >
476 >
477 field <
478 name: "nested"
479 number: 1
480 label: LABEL_OPTIONAL
481 type: TYPE_MESSAGE
482 type_name: "StringMessage"
483 >
484 >
485 service <
486 name: "ExampleService"
487 method <
488 name: "Echo"
489 input_type: "OuterMessage"
490 output_type: "OuterMessage"
491 options <
492 [google.api.http] <
493 post: "/v1/example/echo"
494 body: "nested"
495 >
496 >
497 >
498 >
499 `
500 var fd descriptor.FileDescriptorProto
501 if err := proto.UnmarshalText(src, &fd); err != nil {
502 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
503 }
504 msg := &Message{
505 DescriptorProto: fd.MessageType[0],
506 Fields: []*Field{
507 {
508 FieldDescriptorProto: fd.MessageType[0].Field[0],
509 },
510 },
511 }
512 file := &File{
513 FileDescriptorProto: &fd,
514 GoPkg: GoPackage{
515 Path: "path/to/example.pb",
516 Name: "example_pb",
517 },
518 Messages: []*Message{msg},
519 Services: []*Service{
520 {
521 ServiceDescriptorProto: fd.Service[0],
522 Methods: []*Method{
523 {
524 MethodDescriptorProto: fd.Service[0].Method[0],
525 RequestType: msg,
526 ResponseType: msg,
527 Bindings: []*Binding{
528 {
529 PathTmpl: compilePath(t, "/v1/example/echo"),
530 HTTPMethod: "POST",
531 Body: &Body{
532 FieldPath: FieldPath{
533 {
534 Name: "nested",
535 Target: msg.Fields[0],
536 },
537 },
538 },
539 },
540 },
541 },
542 },
543 },
544 },
545 }
546
547 crossLinkFixture(file)
548 testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
549 }
550
551 func TestExtractServicesWithPathParam(t *testing.T) {
552 src := `
553 name: "path/to/example.proto",
554 package: "example"
555 message_type <
556 name: "StringMessage"
557 field <
558 name: "string"
559 number: 1
560 label: LABEL_OPTIONAL
561 type: TYPE_STRING
562 >
563 >
564 service <
565 name: "ExampleService"
566 method <
567 name: "Echo"
568 input_type: "StringMessage"
569 output_type: "StringMessage"
570 options <
571 [google.api.http] <
572 get: "/v1/example/echo/{string=*}"
573 >
574 >
575 >
576 >
577 `
578 var fd descriptor.FileDescriptorProto
579 if err := proto.UnmarshalText(src, &fd); err != nil {
580 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
581 }
582 msg := &Message{
583 DescriptorProto: fd.MessageType[0],
584 Fields: []*Field{
585 {
586 FieldDescriptorProto: fd.MessageType[0].Field[0],
587 },
588 },
589 }
590 file := &File{
591 FileDescriptorProto: &fd,
592 GoPkg: GoPackage{
593 Path: "path/to/example.pb",
594 Name: "example_pb",
595 },
596 Messages: []*Message{msg},
597 Services: []*Service{
598 {
599 ServiceDescriptorProto: fd.Service[0],
600 Methods: []*Method{
601 {
602 MethodDescriptorProto: fd.Service[0].Method[0],
603 RequestType: msg,
604 ResponseType: msg,
605 Bindings: []*Binding{
606 {
607 PathTmpl: compilePath(t, "/v1/example/echo/{string=*}"),
608 HTTPMethod: "GET",
609 PathParams: []Parameter{
610 {
611 FieldPath: FieldPath{
612 {
613 Name: "string",
614 Target: msg.Fields[0],
615 },
616 },
617 Target: msg.Fields[0],
618 },
619 },
620 },
621 },
622 },
623 },
624 },
625 },
626 }
627
628 crossLinkFixture(file)
629 testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
630 }
631
632 func TestExtractServicesWithAdditionalBinding(t *testing.T) {
633 src := `
634 name: "path/to/example.proto",
635 package: "example"
636 message_type <
637 name: "StringMessage"
638 field <
639 name: "string"
640 number: 1
641 label: LABEL_OPTIONAL
642 type: TYPE_STRING
643 >
644 >
645 service <
646 name: "ExampleService"
647 method <
648 name: "Echo"
649 input_type: "StringMessage"
650 output_type: "StringMessage"
651 options <
652 [google.api.http] <
653 post: "/v1/example/echo"
654 body: "*"
655 additional_bindings <
656 get: "/v1/example/echo/{string}"
657 >
658 additional_bindings <
659 post: "/v2/example/echo"
660 body: "string"
661 >
662 >
663 >
664 >
665 >
666 `
667 var fd descriptor.FileDescriptorProto
668 if err := proto.UnmarshalText(src, &fd); err != nil {
669 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
670 }
671 msg := &Message{
672 DescriptorProto: fd.MessageType[0],
673 Fields: []*Field{
674 {
675 FieldDescriptorProto: fd.MessageType[0].Field[0],
676 },
677 },
678 }
679 file := &File{
680 FileDescriptorProto: &fd,
681 GoPkg: GoPackage{
682 Path: "path/to/example.pb",
683 Name: "example_pb",
684 },
685 Messages: []*Message{msg},
686 Services: []*Service{
687 {
688 ServiceDescriptorProto: fd.Service[0],
689 Methods: []*Method{
690 {
691 MethodDescriptorProto: fd.Service[0].Method[0],
692 RequestType: msg,
693 ResponseType: msg,
694 Bindings: []*Binding{
695 {
696 Index: 0,
697 PathTmpl: compilePath(t, "/v1/example/echo"),
698 HTTPMethod: "POST",
699 Body: &Body{FieldPath: nil},
700 },
701 {
702 Index: 1,
703 PathTmpl: compilePath(t, "/v1/example/echo/{string}"),
704 HTTPMethod: "GET",
705 PathParams: []Parameter{
706 {
707 FieldPath: FieldPath{
708 {
709 Name: "string",
710 Target: msg.Fields[0],
711 },
712 },
713 Target: msg.Fields[0],
714 },
715 },
716 Body: nil,
717 },
718 {
719 Index: 2,
720 PathTmpl: compilePath(t, "/v2/example/echo"),
721 HTTPMethod: "POST",
722 Body: &Body{
723 FieldPath: FieldPath{
724 FieldPathComponent{
725 Name: "string",
726 Target: msg.Fields[0],
727 },
728 },
729 },
730 },
731 },
732 },
733 },
734 },
735 },
736 }
737
738 crossLinkFixture(file)
739 testExtractServices(t, []*descriptor.FileDescriptorProto{&fd}, "path/to/example.proto", file.Services)
740 }
741
742 func TestExtractServicesWithError(t *testing.T) {
743 for _, spec := range []struct {
744 target string
745 srcs []string
746 }{
747 {
748 target: "path/to/example.proto",
749 srcs: []string{
750
751 `
752 name: "path/to/example.proto",
753 package: "example"
754 service <
755 name: "ExampleService"
756 method <
757 name: "Echo"
758 input_type: "StringMessage"
759 output_type: "StringMessage"
760 options <
761 [google.api.http] <
762 post: "/v1/example/echo"
763 body: "*"
764 >
765 >
766 >
767 >
768 `,
769 },
770 },
771
772 {
773 target: "path/to/example.proto",
774 srcs: []string{`
775 name: "path/to/example.proto",
776 package: "example"
777 message_type <
778 name: "StringMessage"
779 field <
780 name: "string"
781 number: 1
782 label: LABEL_OPTIONAL
783 type: TYPE_STRING
784 >
785 >
786 service <
787 name: "ExampleService"
788 method <
789 name: "Echo"
790 input_type: "StringMessage"
791 output_type: "StringMessage"
792 options <
793 [google.api.http] <
794 post: "/v1/example/echo"
795 body: "bool"
796 >
797 >
798 >
799 >`,
800 },
801 },
802
803 {
804 target: "path/to/example.proto",
805 srcs: []string{
806 `
807 name: "path/to/example.proto",
808 package: "example"
809 message_type <
810 name: "StringMessage"
811 field <
812 name: "string"
813 number: 1
814 label: LABEL_OPTIONAL
815 type: TYPE_STRING
816 >
817 >
818 service <
819 name: "ExampleService"
820 method <
821 name: "Echo"
822 input_type: "StringMessage"
823 output_type: "StringMessage"
824 options <
825 [google.api.http] <
826 post: "/v1/example/echo/{bool=*}"
827 >
828 >
829 >
830 >
831 `,
832 },
833 },
834
835 {
836 target: "path/to/example.proto",
837 srcs: []string{
838 `
839 name: "path/to/example.proto",
840 package: "example"
841 message_type <
842 name: "OuterMessage"
843 field <
844 name: "mid"
845 number: 1
846 label: LABEL_OPTIONAL
847 type: TYPE_STRING
848 >
849 field <
850 name: "bool"
851 number: 2
852 label: LABEL_OPTIONAL
853 type: TYPE_BOOL
854 >
855 >
856 service <
857 name: "ExampleService"
858 method <
859 name: "Echo"
860 input_type: "OuterMessage"
861 output_type: "OuterMessage"
862 options <
863 [google.api.http] <
864 post: "/v1/example/echo/{mid.bool=*}"
865 >
866 >
867 >
868 >
869 `,
870 },
871 },
872
873 {
874 target: "path/to/example.proto",
875 srcs: []string{
876 `
877 name: "path/to/example.proto",
878 package: "example"
879 message_type <
880 name: "StringMessage"
881 field <
882 name: "string"
883 number: 1
884 label: LABEL_OPTIONAL
885 type: TYPE_STRING
886 >
887 >
888 service <
889 name: "ExampleService"
890 method <
891 name: "Echo"
892 input_type: "StringMessage"
893 output_type: "StringMessage"
894 options <
895 [google.api.http] <
896 post: "/v1/example/echo/{bool=*}"
897 >
898 >
899 client_streaming: true
900 >
901 >
902 `,
903 },
904 },
905
906 {
907 target: "path/to/example.proto",
908 srcs: []string{
909 `
910 name: "path/to/example.proto",
911 package: "example"
912 message_type <
913 name: "StringMessage"
914 field <
915 name: "string"
916 number: 1
917 label: LABEL_OPTIONAL
918 type: TYPE_STRING
919 >
920 >
921 service <
922 name: "ExampleService"
923 method <
924 name: "Echo"
925 input_type: "StringMessage"
926 output_type: "StringMessage"
927 options <
928 [google.api.http] <
929 get: "/v1/example/echo"
930 body: "string"
931 >
932 >
933 >
934 >
935 `,
936 },
937 },
938
939 {
940 target: "path/to/example.proto",
941 srcs: []string{
942 `
943 name: "path/to/example.proto",
944 package: "example"
945 message_type <
946 name: "StringMessage"
947 field <
948 name: "string"
949 number: 1
950 label: LABEL_OPTIONAL
951 type: TYPE_STRING
952 >
953 >
954 service <
955 name: "ExampleService"
956 method <
957 name: "RemoveResource"
958 input_type: "StringMessage"
959 output_type: "StringMessage"
960 options <
961 [google.api.http] <
962 delete: "/v1/example/resource"
963 body: "string"
964 >
965 >
966 >
967 >
968 `,
969 },
970 },
971
972 {
973 target: "path/to/example.proto",
974 srcs: []string{
975 `
976 name: "path/to/example.proto",
977 package: "example"
978 service <
979 name: "ExampleService"
980 method <
981 name: "RemoveResource"
982 input_type: "StringMessage"
983 output_type: "StringMessage"
984 options <
985 [google.api.http] <
986 body: "string"
987 >
988 >
989 >
990 >
991 `,
992 },
993 },
994
995 {
996 target: "path/to/example.proto",
997 srcs: []string{`
998 name: "path/to/example.proto",
999 package: "example"
1000 message_type <
1001 name: "OuterMessage"
1002 nested_type <
1003 name: "StringMessage"
1004 field <
1005 name: "value"
1006 number: 1
1007 label: LABEL_OPTIONAL
1008 type: TYPE_STRING
1009 >
1010 >
1011 field <
1012 name: "string"
1013 number: 1
1014 label: LABEL_OPTIONAL
1015 type: TYPE_MESSAGE
1016 type_name: "StringMessage"
1017 >
1018 >
1019 service <
1020 name: "ExampleService"
1021 method <
1022 name: "Echo"
1023 input_type: "OuterMessage"
1024 output_type: "OuterMessage"
1025 options <
1026 [google.api.http] <
1027 get: "/v1/example/echo/{string=*}"
1028 >
1029 >
1030 >
1031 >
1032 `,
1033 },
1034 },
1035 } {
1036 reg := NewRegistry()
1037
1038 var fds []*descriptor.FileDescriptorProto
1039 for _, src := range spec.srcs {
1040 var fd descriptor.FileDescriptorProto
1041 if err := proto.UnmarshalText(src, &fd); err != nil {
1042 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
1043 }
1044 reg.loadFile(&fd)
1045 fds = append(fds, &fd)
1046 }
1047 err := reg.loadServices(reg.files[spec.target])
1048 if err == nil {
1049 t.Errorf("loadServices(%q) succeeded; want an error; files=%v", spec.target, spec.srcs)
1050 }
1051 t.Log(err)
1052 }
1053 }
1054
1055 func TestResolveFieldPath(t *testing.T) {
1056 for _, spec := range []struct {
1057 src string
1058 path string
1059 wantErr bool
1060 }{
1061 {
1062 src: `
1063 name: 'example.proto'
1064 package: 'example'
1065 message_type <
1066 name: 'ExampleMessage'
1067 field <
1068 name: 'string'
1069 type: TYPE_STRING
1070 label: LABEL_OPTIONAL
1071 number: 1
1072 >
1073 >
1074 `,
1075 path: "string",
1076 wantErr: false,
1077 },
1078
1079 {
1080 src: `
1081 name: 'example.proto'
1082 package: 'example'
1083 message_type <
1084 name: 'ExampleMessage'
1085 field <
1086 name: 'string'
1087 type: TYPE_STRING
1088 label: LABEL_OPTIONAL
1089 number: 1
1090 >
1091 >
1092 `,
1093 path: "something_else",
1094 wantErr: true,
1095 },
1096
1097 {
1098 src: `
1099 name: 'example.proto'
1100 package: 'example'
1101 message_type <
1102 name: 'ExampleMessage'
1103 field <
1104 name: 'string'
1105 type: TYPE_STRING
1106 label: LABEL_REPEATED
1107 number: 1
1108 >
1109 >
1110 `,
1111 path: "string",
1112 wantErr: true,
1113 },
1114
1115 {
1116 src: `
1117 name: 'example.proto'
1118 package: 'example'
1119 message_type <
1120 name: 'ExampleMessage'
1121 field <
1122 name: 'nested'
1123 type: TYPE_MESSAGE
1124 type_name: 'AnotherMessage'
1125 label: LABEL_OPTIONAL
1126 number: 1
1127 >
1128 field <
1129 name: 'terminal'
1130 type: TYPE_BOOL
1131 label: LABEL_OPTIONAL
1132 number: 2
1133 >
1134 >
1135 message_type <
1136 name: 'AnotherMessage'
1137 field <
1138 name: 'nested2'
1139 type: TYPE_MESSAGE
1140 type_name: 'ExampleMessage'
1141 label: LABEL_OPTIONAL
1142 number: 1
1143 >
1144 >
1145 `,
1146 path: "nested.nested2.nested.nested2.nested.nested2.terminal",
1147 wantErr: false,
1148 },
1149
1150 {
1151 src: `
1152 name: 'example.proto'
1153 package: 'example'
1154 message_type <
1155 name: 'ExampleMessage'
1156 field <
1157 name: 'nested'
1158 type: TYPE_MESSAGE
1159 type_name: 'AnotherMessage'
1160 label: LABEL_OPTIONAL
1161 number: 1
1162 >
1163 field <
1164 name: 'terminal'
1165 type: TYPE_BOOL
1166 label: LABEL_OPTIONAL
1167 number: 2
1168 >
1169 >
1170 message_type <
1171 name: 'AnotherMessage'
1172 field <
1173 name: 'nested2'
1174 type: TYPE_MESSAGE
1175 type_name: 'ExampleMessage'
1176 label: LABEL_OPTIONAL
1177 number: 1
1178 >
1179 >
1180 `,
1181 path: "nested.terminal.nested2",
1182 wantErr: true,
1183 },
1184
1185 {
1186 src: `
1187 name: 'example.proto'
1188 package: 'example'
1189 message_type <
1190 name: 'ExampleMessage'
1191 field <
1192 name: 'nested'
1193 type: TYPE_MESSAGE
1194 type_name: 'AnotherMessage'
1195 label: LABEL_OPTIONAL
1196 number: 1
1197 >
1198 field <
1199 name: 'terminal'
1200 type: TYPE_BOOL
1201 label: LABEL_OPTIONAL
1202 number: 2
1203 >
1204 >
1205 message_type <
1206 name: 'AnotherMessage'
1207 field <
1208 name: 'nested2'
1209 type: TYPE_MESSAGE
1210 type_name: 'ExampleMessage'
1211 label: LABEL_REPEATED
1212 number: 1
1213 >
1214 >
1215 `,
1216 path: "nested.nested2.terminal",
1217 wantErr: true,
1218 },
1219 } {
1220 var file descriptor.FileDescriptorProto
1221 if err := proto.UnmarshalText(spec.src, &file); err != nil {
1222 t.Fatalf("proto.Unmarshal(%s) failed with %v; want success", spec.src, err)
1223 }
1224 reg := NewRegistry()
1225 reg.loadFile(&file)
1226 f, err := reg.LookupFile(file.GetName())
1227 if err != nil {
1228 t.Fatalf("reg.LookupFile(%q) failed with %v; want success; on file=%s", file.GetName(), err, spec.src)
1229 }
1230 _, err = reg.resolveFieldPath(f.Messages[0], spec.path, false)
1231 if got, want := err != nil, spec.wantErr; got != want {
1232 if want {
1233 t.Errorf("reg.resolveFiledPath(%q, %q) succeeded; want an error", f.Messages[0].GetName(), spec.path)
1234 continue
1235 }
1236 t.Errorf("reg.resolveFiledPath(%q, %q) failed with %v; want success", f.Messages[0].GetName(), spec.path, err)
1237 }
1238 }
1239 }
1240
1241 func TestExtractServicesWithDeleteBody(t *testing.T) {
1242 for _, spec := range []struct {
1243 allowDeleteBody bool
1244 expectErr bool
1245 target string
1246 srcs []string
1247 }{
1248
1249 {
1250 allowDeleteBody: true,
1251 expectErr: false,
1252 target: "path/to/example.proto",
1253 srcs: []string{
1254 `
1255 name: "path/to/example.proto",
1256 package: "example"
1257 message_type <
1258 name: "StringMessage"
1259 field <
1260 name: "string"
1261 number: 1
1262 label: LABEL_OPTIONAL
1263 type: TYPE_STRING
1264 >
1265 >
1266 service <
1267 name: "ExampleService"
1268 method <
1269 name: "RemoveResource"
1270 input_type: "StringMessage"
1271 output_type: "StringMessage"
1272 options <
1273 [google.api.http] <
1274 delete: "/v1/example/resource"
1275 body: "string"
1276 >
1277 >
1278 >
1279 >
1280 `,
1281 },
1282 },
1283
1284 {
1285 allowDeleteBody: false,
1286 expectErr: true,
1287 target: "path/to/example.proto",
1288 srcs: []string{
1289 `
1290 name: "path/to/example.proto",
1291 package: "example"
1292 message_type <
1293 name: "StringMessage"
1294 field <
1295 name: "string"
1296 number: 1
1297 label: LABEL_OPTIONAL
1298 type: TYPE_STRING
1299 >
1300 >
1301 service <
1302 name: "ExampleService"
1303 method <
1304 name: "RemoveResource"
1305 input_type: "StringMessage"
1306 output_type: "StringMessage"
1307 options <
1308 [google.api.http] <
1309 delete: "/v1/example/resource"
1310 body: "string"
1311 >
1312 >
1313 >
1314 >
1315 `,
1316 },
1317 },
1318 } {
1319 reg := NewRegistry()
1320 reg.SetAllowDeleteBody(spec.allowDeleteBody)
1321
1322 var fds []*descriptor.FileDescriptorProto
1323 for _, src := range spec.srcs {
1324 var fd descriptor.FileDescriptorProto
1325 if err := proto.UnmarshalText(src, &fd); err != nil {
1326 t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
1327 }
1328 reg.loadFile(&fd)
1329 fds = append(fds, &fd)
1330 }
1331 err := reg.loadServices(reg.files[spec.target])
1332 if spec.expectErr && err == nil {
1333 t.Errorf("loadServices(%q) succeeded; want an error; allowDeleteBody=%v, files=%v", spec.target, spec.allowDeleteBody, spec.srcs)
1334 }
1335 if !spec.expectErr && err != nil {
1336 t.Errorf("loadServices(%q) failed; do not want an error; allowDeleteBody=%v, files=%v", spec.target, spec.allowDeleteBody, spec.srcs)
1337 }
1338 t.Log(err)
1339 }
1340 }
1341
View as plain text