registry_test.go 20 KB

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