gen_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. package gogen
  2. import (
  3. goformat "go/format"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "testing"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/tal-tech/go-zero/tools/goctl/api/parser"
  11. "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
  12. )
  13. const testApiTemplate = `
  14. info(
  15. title: doc title
  16. desc: >
  17. doc description first part,
  18. doc description second part<
  19. version: 1.0
  20. )
  21. type Request struct {
  22. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  23. }
  24. type Response struct {
  25. Message string ` + "`" + `json:"message"` + "`" + `
  26. }
  27. @server(
  28. // C0
  29. group: greet/s1
  30. )
  31. // C1
  32. service A-api {
  33. // C2
  34. @server( // C3
  35. handler: GreetHandler
  36. )
  37. get /greet/from/:name(Request) returns (Response) // hello
  38. // C4
  39. @handler NoResponseHandler // C5
  40. get /greet/get(Request)
  41. }
  42. `
  43. const testMultiServiceTemplate = `
  44. info(
  45. title: doc title
  46. desc: >
  47. doc description first part,
  48. doc description second part<
  49. version: 1.0
  50. )
  51. type Request struct {
  52. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  53. }
  54. type Response struct {
  55. Message string ` + "`" + `json:"message"` + "`" + `
  56. }
  57. service A-api {
  58. @server(
  59. handler: GreetHandler
  60. )
  61. get /greet/from/:name(Request) returns (Response)
  62. }
  63. service A-api {
  64. @server(
  65. handler: NoResponseHandler
  66. )
  67. get /greet/get(Request) returns
  68. }
  69. `
  70. const apiNoInfo = `
  71. type Request struct {
  72. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  73. }
  74. type Response struct {
  75. Message string ` + "`" + `json:"message"` + "`" + `
  76. }
  77. service A-api {
  78. @server(
  79. handler: GreetHandler
  80. )
  81. get /greet/from/:name(Request) returns (Response)
  82. }
  83. `
  84. const invalidApiFile = `
  85. type Request struct {
  86. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  87. }
  88. type Response struct {
  89. Message string ` + "`" + `json:"message"` + "`" + `
  90. }
  91. service A-api
  92. @server(
  93. handler: GreetHandler
  94. )
  95. get /greet/from/:name(Request) returns (Response)
  96. }
  97. `
  98. const anonymousAnnotation = `
  99. type Request struct {
  100. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  101. }
  102. type Response struct {
  103. Message string ` + "`" + `json:"message"` + "`" + `
  104. }
  105. service A-api {
  106. @handler GreetHandler
  107. get /greet/from/:name(Request) returns (Response)
  108. }
  109. `
  110. const apiHasMiddleware = `
  111. type Request struct {
  112. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  113. }
  114. type Response struct {
  115. Message string ` + "`" + `json:"message"` + "`" + `
  116. }
  117. @server(
  118. middleware: TokenValidate
  119. )
  120. service A-api {
  121. @handler GreetHandler
  122. get /greet/from/:name(Request) returns (Response)
  123. }
  124. `
  125. const apiJwt = `
  126. type Request struct {
  127. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  128. }
  129. type Response struct {
  130. Message string ` + "`" + `json:"message"` + "`" + `
  131. }
  132. @server(
  133. jwt: Auth
  134. )
  135. service A-api {
  136. @handler GreetHandler
  137. get /greet/from/:name(Request) returns (Response)
  138. }
  139. `
  140. const apiJwtWithMiddleware = `
  141. type Request struct {
  142. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  143. }
  144. type Response struct {
  145. Message string ` + "`" + `json:"message"` + "`" + `
  146. }
  147. @server(
  148. jwt: Auth
  149. middleware: TokenValidate
  150. )
  151. service A-api {
  152. @handler GreetHandler
  153. get /greet/from/:name(Request) returns (Response)
  154. }
  155. `
  156. const apiHasNoRequest = `
  157. service A-api {
  158. @handler GreetHandler
  159. post /greet/ping ()
  160. }
  161. `
  162. const apiRouteTest = `
  163. type Request struct {
  164. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  165. }
  166. type Response struct {
  167. Message string ` + "`" + `json:"message"` + "`" + `
  168. }
  169. service A-api {
  170. @handler NormalHandler
  171. get /greet/from/:name(Request) returns (Response)
  172. @handler NoResponseHandler
  173. get /greet/from/:sex(Request)
  174. @handler NoRequestHandler
  175. get /greet/from/request returns (Response)
  176. @handler NoRequestNoResponseHandler
  177. get /greet/from
  178. }
  179. `
  180. const hasCommentApiTest = `
  181. type Inline struct {
  182. }
  183. type Request struct {
  184. Inline
  185. Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // name in path
  186. }
  187. type Response struct {
  188. Message string ` + "`" + `json:"msg"` + "`" + ` // message
  189. }
  190. service A-api {
  191. @doc(helloworld)
  192. @server(
  193. handler: GreetHandler
  194. )
  195. get /greet/from/:name(Request) returns (Response)
  196. }
  197. `
  198. const hasInlineNoExistTest = `
  199. type Request struct {
  200. Inline
  201. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  202. }
  203. type Response struct {
  204. Message string ` + "`" + `json:"message"` + "`" + ` // message
  205. }
  206. service A-api {
  207. @doc(helloworld)
  208. @server(
  209. handler: GreetHandler
  210. )
  211. get /greet/from/:name(Request) returns (Response)
  212. }
  213. `
  214. const importApi = `
  215. type ImportData struct {
  216. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  217. }
  218. `
  219. const hasImportApi = `
  220. import "importApi.api"
  221. type Request struct {
  222. Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
  223. }
  224. type Response struct {
  225. Message string ` + "`" + `json:"message"` + "`" + ` // message
  226. }
  227. service A-api {
  228. @server(
  229. handler: GreetHandler
  230. )
  231. get /greet/from/:name(Request) returns (Response)
  232. }
  233. `
  234. func TestParser(t *testing.T) {
  235. filename := "greet.api"
  236. err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
  237. assert.Nil(t, err)
  238. defer os.Remove(filename)
  239. parser, err := parser.NewParser(filename)
  240. assert.Nil(t, err)
  241. api, err := parser.Parse()
  242. assert.Nil(t, err)
  243. assert.Equal(t, len(api.Types), 2)
  244. assert.Equal(t, len(api.Service.Routes), 2)
  245. assert.Equal(t, api.Service.Routes[0].Path, "/greet/from/:name")
  246. assert.Equal(t, api.Service.Routes[1].Path, "/greet/get")
  247. assert.Equal(t, api.Service.Routes[1].RequestType.Name, "Request")
  248. assert.Equal(t, api.Service.Routes[1].ResponseType.Name, "")
  249. validate(t, filename)
  250. }
  251. func TestMultiService(t *testing.T) {
  252. filename := "greet.api"
  253. err := ioutil.WriteFile(filename, []byte(testMultiServiceTemplate), os.ModePerm)
  254. assert.Nil(t, err)
  255. defer os.Remove(filename)
  256. parser, err := parser.NewParser(filename)
  257. assert.Nil(t, err)
  258. api, err := parser.Parse()
  259. assert.Nil(t, err)
  260. assert.Equal(t, len(api.Service.Routes), 2)
  261. assert.Equal(t, len(api.Service.Groups), 2)
  262. validate(t, filename)
  263. }
  264. func TestApiNoInfo(t *testing.T) {
  265. filename := "greet.api"
  266. err := ioutil.WriteFile(filename, []byte(apiNoInfo), os.ModePerm)
  267. assert.Nil(t, err)
  268. defer os.Remove(filename)
  269. parser, err := parser.NewParser(filename)
  270. assert.Nil(t, err)
  271. _, err = parser.Parse()
  272. assert.Nil(t, err)
  273. validate(t, filename)
  274. }
  275. func TestInvalidApiFile(t *testing.T) {
  276. filename := "greet.api"
  277. err := ioutil.WriteFile(filename, []byte(invalidApiFile), os.ModePerm)
  278. assert.Nil(t, err)
  279. defer os.Remove(filename)
  280. parser, err := parser.NewParser(filename)
  281. assert.Nil(t, err)
  282. _, err = parser.Parse()
  283. assert.NotNil(t, err)
  284. }
  285. func TestAnonymousAnnotation(t *testing.T) {
  286. filename := "greet.api"
  287. err := ioutil.WriteFile(filename, []byte(anonymousAnnotation), os.ModePerm)
  288. assert.Nil(t, err)
  289. defer os.Remove(filename)
  290. parser, err := parser.NewParser(filename)
  291. assert.Nil(t, err)
  292. api, err := parser.Parse()
  293. assert.Nil(t, err)
  294. assert.Equal(t, len(api.Service.Routes), 1)
  295. assert.Equal(t, api.Service.Routes[0].Annotations[0].Value, "GreetHandler")
  296. validate(t, filename)
  297. }
  298. func TestApiHasMiddleware(t *testing.T) {
  299. filename := "greet.api"
  300. err := ioutil.WriteFile(filename, []byte(apiHasMiddleware), os.ModePerm)
  301. assert.Nil(t, err)
  302. defer os.Remove(filename)
  303. parser, err := parser.NewParser(filename)
  304. assert.Nil(t, err)
  305. _, err = parser.Parse()
  306. assert.Nil(t, err)
  307. validate(t, filename)
  308. }
  309. func TestApiHasJwt(t *testing.T) {
  310. filename := "jwt.api"
  311. err := ioutil.WriteFile(filename, []byte(apiJwt), os.ModePerm)
  312. assert.Nil(t, err)
  313. defer os.Remove(filename)
  314. parser, err := parser.NewParser(filename)
  315. assert.Nil(t, err)
  316. _, err = parser.Parse()
  317. assert.Nil(t, err)
  318. validate(t, filename)
  319. }
  320. func TestApiHasJwtAndMiddleware(t *testing.T) {
  321. filename := "jwt.api"
  322. err := ioutil.WriteFile(filename, []byte(apiJwtWithMiddleware), os.ModePerm)
  323. assert.Nil(t, err)
  324. defer os.Remove(filename)
  325. parser, err := parser.NewParser(filename)
  326. assert.Nil(t, err)
  327. _, err = parser.Parse()
  328. assert.Nil(t, err)
  329. validate(t, filename)
  330. }
  331. func TestApiHasNoRequestBody(t *testing.T) {
  332. filename := "greet.api"
  333. err := ioutil.WriteFile(filename, []byte(apiHasNoRequest), os.ModePerm)
  334. assert.Nil(t, err)
  335. defer os.Remove(filename)
  336. parser, err := parser.NewParser(filename)
  337. assert.Nil(t, err)
  338. _, err = parser.Parse()
  339. assert.Nil(t, err)
  340. validate(t, filename)
  341. }
  342. func TestApiRoutes(t *testing.T) {
  343. filename := "greet.api"
  344. err := ioutil.WriteFile(filename, []byte(apiRouteTest), os.ModePerm)
  345. assert.Nil(t, err)
  346. defer os.Remove(filename)
  347. parser, err := parser.NewParser(filename)
  348. assert.Nil(t, err)
  349. _, err = parser.Parse()
  350. assert.Nil(t, err)
  351. validate(t, filename)
  352. }
  353. func TestHasCommentRoutes(t *testing.T) {
  354. filename := "greet.api"
  355. err := ioutil.WriteFile(filename, []byte(hasCommentApiTest), os.ModePerm)
  356. assert.Nil(t, err)
  357. defer os.Remove(filename)
  358. parser, err := parser.NewParser(filename)
  359. assert.Nil(t, err)
  360. _, err = parser.Parse()
  361. assert.Nil(t, err)
  362. validate(t, filename)
  363. }
  364. func TestInlineTypeNotExist(t *testing.T) {
  365. filename := "greet.api"
  366. err := ioutil.WriteFile(filename, []byte(hasInlineNoExistTest), os.ModePerm)
  367. assert.Nil(t, err)
  368. defer os.Remove(filename)
  369. parser, err := parser.NewParser(filename)
  370. assert.Nil(t, err)
  371. _, err = parser.Parse()
  372. assert.Nil(t, err)
  373. validate(t, filename)
  374. }
  375. func TestHasImportApi(t *testing.T) {
  376. filename := "greet.api"
  377. err := ioutil.WriteFile(filename, []byte(hasImportApi), os.ModePerm)
  378. assert.Nil(t, err)
  379. defer os.Remove(filename)
  380. importApiName := "importApi.api"
  381. err = ioutil.WriteFile(importApiName, []byte(importApi), os.ModePerm)
  382. assert.Nil(t, err)
  383. defer os.Remove(importApiName)
  384. parser, err := parser.NewParser(filename)
  385. assert.Nil(t, err)
  386. api, err := parser.Parse()
  387. assert.Nil(t, err)
  388. var hasInline bool
  389. for _, ty := range api.Types {
  390. if ty.Name == "ImportData" {
  391. hasInline = true
  392. break
  393. }
  394. }
  395. assert.True(t, hasInline)
  396. validate(t, filename)
  397. }
  398. func validate(t *testing.T, api string) {
  399. dir := "_go"
  400. os.RemoveAll(dir)
  401. err := DoGenProject(api, dir)
  402. defer os.RemoveAll(dir)
  403. assert.Nil(t, err)
  404. filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  405. if strings.HasSuffix(path, ".go") {
  406. code, err := ioutil.ReadFile(path)
  407. assert.Nil(t, err)
  408. assert.Nil(t, validateCode(string(code)))
  409. }
  410. return nil
  411. })
  412. _, err = execx.Run("go test ./...", dir)
  413. assert.Nil(t, err)
  414. }
  415. func validateCode(code string) error {
  416. _, err := goformat.Source([]byte(code))
  417. return err
  418. }