services_test.go 34 KB

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