routes_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
  2. // Use of this source code is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package gin
  5. import (
  6. "fmt"
  7. "io/ioutil"
  8. "net/http"
  9. "net/http/httptest"
  10. "os"
  11. "path/filepath"
  12. "testing"
  13. "github.com/stretchr/testify/assert"
  14. )
  15. func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
  16. req, _ := http.NewRequest(method, path, nil)
  17. w := httptest.NewRecorder()
  18. r.ServeHTTP(w, req)
  19. return w
  20. }
  21. func testRouteOK(method string, t *testing.T) {
  22. passed := false
  23. passedAny := false
  24. r := New()
  25. r.Any("/test2", func(c *Context) {
  26. passedAny = true
  27. })
  28. r.Handle(method, "/test", func(c *Context) {
  29. passed = true
  30. })
  31. w := performRequest(r, method, "/test")
  32. assert.True(t, passed)
  33. assert.Equal(t, http.StatusOK, w.Code)
  34. performRequest(r, method, "/test2")
  35. assert.True(t, passedAny)
  36. }
  37. // TestSingleRouteOK tests that POST route is correctly invoked.
  38. func testRouteNotOK(method string, t *testing.T) {
  39. passed := false
  40. router := New()
  41. router.Handle(method, "/test_2", func(c *Context) {
  42. passed = true
  43. })
  44. w := performRequest(router, method, "/test")
  45. assert.False(t, passed)
  46. assert.Equal(t, http.StatusNotFound, w.Code)
  47. }
  48. // TestSingleRouteOK tests that POST route is correctly invoked.
  49. func testRouteNotOK2(method string, t *testing.T) {
  50. passed := false
  51. router := New()
  52. router.HandleMethodNotAllowed = true
  53. var methodRoute string
  54. if method == "POST" {
  55. methodRoute = "GET"
  56. } else {
  57. methodRoute = "POST"
  58. }
  59. router.Handle(methodRoute, "/test", func(c *Context) {
  60. passed = true
  61. })
  62. w := performRequest(router, method, "/test")
  63. assert.False(t, passed)
  64. assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
  65. }
  66. func TestRouterMethod(t *testing.T) {
  67. router := New()
  68. router.PUT("/hey2", func(c *Context) {
  69. c.String(http.StatusOK, "sup2")
  70. })
  71. router.PUT("/hey", func(c *Context) {
  72. c.String(http.StatusOK, "called")
  73. })
  74. router.PUT("/hey3", func(c *Context) {
  75. c.String(http.StatusOK, "sup3")
  76. })
  77. w := performRequest(router, "PUT", "/hey")
  78. assert.Equal(t, http.StatusOK, w.Code)
  79. assert.Equal(t, "called", w.Body.String())
  80. }
  81. func TestRouterGroupRouteOK(t *testing.T) {
  82. testRouteOK("GET", t)
  83. testRouteOK("POST", t)
  84. testRouteOK("PUT", t)
  85. testRouteOK("PATCH", t)
  86. testRouteOK("HEAD", t)
  87. testRouteOK("OPTIONS", t)
  88. testRouteOK("DELETE", t)
  89. testRouteOK("CONNECT", t)
  90. testRouteOK("TRACE", t)
  91. }
  92. func TestRouteNotOK(t *testing.T) {
  93. testRouteNotOK("GET", t)
  94. testRouteNotOK("POST", t)
  95. testRouteNotOK("PUT", t)
  96. testRouteNotOK("PATCH", t)
  97. testRouteNotOK("HEAD", t)
  98. testRouteNotOK("OPTIONS", t)
  99. testRouteNotOK("DELETE", t)
  100. testRouteNotOK("CONNECT", t)
  101. testRouteNotOK("TRACE", t)
  102. }
  103. func TestRouteNotOK2(t *testing.T) {
  104. testRouteNotOK2("GET", t)
  105. testRouteNotOK2("POST", t)
  106. testRouteNotOK2("PUT", t)
  107. testRouteNotOK2("PATCH", t)
  108. testRouteNotOK2("HEAD", t)
  109. testRouteNotOK2("OPTIONS", t)
  110. testRouteNotOK2("DELETE", t)
  111. testRouteNotOK2("CONNECT", t)
  112. testRouteNotOK2("TRACE", t)
  113. }
  114. func TestRouteRedirectTrailingSlash(t *testing.T) {
  115. router := New()
  116. router.RedirectFixedPath = false
  117. router.RedirectTrailingSlash = true
  118. router.GET("/path", func(c *Context) {})
  119. router.GET("/path2/", func(c *Context) {})
  120. router.POST("/path3", func(c *Context) {})
  121. router.PUT("/path4/", func(c *Context) {})
  122. w := performRequest(router, "GET", "/path/")
  123. assert.Equal(t, "/path", w.Header().Get("Location"))
  124. assert.Equal(t, http.StatusMovedPermanently, w.Code)
  125. w = performRequest(router, "GET", "/path2")
  126. assert.Equal(t, "/path2/", w.Header().Get("Location"))
  127. assert.Equal(t, http.StatusMovedPermanently, w.Code)
  128. w = performRequest(router, "POST", "/path3/")
  129. assert.Equal(t, "/path3", w.Header().Get("Location"))
  130. assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
  131. w = performRequest(router, "PUT", "/path4")
  132. assert.Equal(t, "/path4/", w.Header().Get("Location"))
  133. assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
  134. w = performRequest(router, "GET", "/path")
  135. assert.Equal(t, http.StatusOK, w.Code)
  136. w = performRequest(router, "GET", "/path2/")
  137. assert.Equal(t, http.StatusOK, w.Code)
  138. w = performRequest(router, "POST", "/path3")
  139. assert.Equal(t, http.StatusOK, w.Code)
  140. w = performRequest(router, "PUT", "/path4/")
  141. assert.Equal(t, http.StatusOK, w.Code)
  142. router.RedirectTrailingSlash = false
  143. w = performRequest(router, "GET", "/path/")
  144. assert.Equal(t, http.StatusNotFound, w.Code)
  145. w = performRequest(router, "GET", "/path2")
  146. assert.Equal(t, http.StatusNotFound, w.Code)
  147. w = performRequest(router, "POST", "/path3/")
  148. assert.Equal(t, http.StatusNotFound, w.Code)
  149. w = performRequest(router, "PUT", "/path4")
  150. assert.Equal(t, http.StatusNotFound, w.Code)
  151. }
  152. func TestRouteRedirectFixedPath(t *testing.T) {
  153. router := New()
  154. router.RedirectFixedPath = true
  155. router.RedirectTrailingSlash = false
  156. router.GET("/path", func(c *Context) {})
  157. router.GET("/Path2", func(c *Context) {})
  158. router.POST("/PATH3", func(c *Context) {})
  159. router.POST("/Path4/", func(c *Context) {})
  160. w := performRequest(router, "GET", "/PATH")
  161. assert.Equal(t, "/path", w.Header().Get("Location"))
  162. assert.Equal(t, http.StatusMovedPermanently, w.Code)
  163. w = performRequest(router, "GET", "/path2")
  164. assert.Equal(t, "/Path2", w.Header().Get("Location"))
  165. assert.Equal(t, http.StatusMovedPermanently, w.Code)
  166. w = performRequest(router, "POST", "/path3")
  167. assert.Equal(t, "/PATH3", w.Header().Get("Location"))
  168. assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
  169. w = performRequest(router, "POST", "/path4")
  170. assert.Equal(t, "/Path4/", w.Header().Get("Location"))
  171. assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
  172. }
  173. // TestContextParamsGet tests that a parameter can be parsed from the URL.
  174. func TestRouteParamsByName(t *testing.T) {
  175. name := ""
  176. lastName := ""
  177. wild := ""
  178. router := New()
  179. router.GET("/test/:name/:last_name/*wild", func(c *Context) {
  180. name = c.Params.ByName("name")
  181. lastName = c.Params.ByName("last_name")
  182. var ok bool
  183. wild, ok = c.Params.Get("wild")
  184. assert.True(t, ok)
  185. assert.Equal(t, name, c.Param("name"))
  186. assert.Equal(t, name, c.Param("name"))
  187. assert.Equal(t, lastName, c.Param("last_name"))
  188. assert.Empty(t, c.Param("wtf"))
  189. assert.Empty(t, c.Params.ByName("wtf"))
  190. wtf, ok := c.Params.Get("wtf")
  191. assert.Empty(t, wtf)
  192. assert.False(t, ok)
  193. })
  194. w := performRequest(router, "GET", "/test/john/smith/is/super/great")
  195. assert.Equal(t, http.StatusOK, w.Code)
  196. assert.Equal(t, "john", name)
  197. assert.Equal(t, "smith", lastName)
  198. assert.Equal(t, "/is/super/great", wild)
  199. }
  200. // TestHandleStaticFile - ensure the static file handles properly
  201. func TestRouteStaticFile(t *testing.T) {
  202. // SETUP file
  203. testRoot, _ := os.Getwd()
  204. f, err := ioutil.TempFile(testRoot, "")
  205. if err != nil {
  206. t.Error(err)
  207. }
  208. defer os.Remove(f.Name())
  209. _, err = f.WriteString("Gin Web Framework")
  210. assert.NoError(t, err)
  211. f.Close()
  212. dir, filename := filepath.Split(f.Name())
  213. // SETUP gin
  214. router := New()
  215. router.Static("/using_static", dir)
  216. router.StaticFile("/result", f.Name())
  217. w := performRequest(router, "GET", "/using_static/"+filename)
  218. w2 := performRequest(router, "GET", "/result")
  219. assert.Equal(t, w, w2)
  220. assert.Equal(t, http.StatusOK, w.Code)
  221. assert.Equal(t, "Gin Web Framework", w.Body.String())
  222. assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
  223. w3 := performRequest(router, "HEAD", "/using_static/"+filename)
  224. w4 := performRequest(router, "HEAD", "/result")
  225. assert.Equal(t, w3, w4)
  226. assert.Equal(t, http.StatusOK, w3.Code)
  227. }
  228. // TestHandleStaticDir - ensure the root/sub dir handles properly
  229. func TestRouteStaticListingDir(t *testing.T) {
  230. router := New()
  231. router.StaticFS("/", Dir("./", true))
  232. w := performRequest(router, "GET", "/")
  233. assert.Equal(t, http.StatusOK, w.Code)
  234. assert.Contains(t, w.Body.String(), "gin.go")
  235. assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
  236. }
  237. // TestHandleHeadToDir - ensure the root/sub dir handles properly
  238. func TestRouteStaticNoListing(t *testing.T) {
  239. router := New()
  240. router.Static("/", "./")
  241. w := performRequest(router, "GET", "/")
  242. assert.Equal(t, http.StatusNotFound, w.Code)
  243. assert.NotContains(t, w.Body.String(), "gin.go")
  244. }
  245. func TestRouterMiddlewareAndStatic(t *testing.T) {
  246. router := New()
  247. static := router.Group("/", func(c *Context) {
  248. c.Writer.Header().Add("Last-Modified", "Mon, 02 Jan 2006 15:04:05 MST")
  249. c.Writer.Header().Add("Expires", "Mon, 02 Jan 2006 15:04:05 MST")
  250. c.Writer.Header().Add("X-GIN", "Gin Framework")
  251. })
  252. static.Static("/", "./")
  253. w := performRequest(router, "GET", "/gin.go")
  254. assert.Equal(t, http.StatusOK, w.Code)
  255. assert.Contains(t, w.Body.String(), "package gin")
  256. assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
  257. assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST")
  258. assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
  259. assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
  260. }
  261. func TestRouteNotAllowedEnabled(t *testing.T) {
  262. router := New()
  263. router.HandleMethodNotAllowed = true
  264. router.POST("/path", func(c *Context) {})
  265. w := performRequest(router, "GET", "/path")
  266. assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
  267. router.NoMethod(func(c *Context) {
  268. c.String(http.StatusTeapot, "responseText")
  269. })
  270. w = performRequest(router, "GET", "/path")
  271. assert.Equal(t, "responseText", w.Body.String())
  272. assert.Equal(t, http.StatusTeapot, w.Code)
  273. }
  274. func TestRouteNotAllowedEnabled2(t *testing.T) {
  275. router := New()
  276. router.HandleMethodNotAllowed = true
  277. // add one methodTree to trees
  278. router.addRoute("POST", "/", HandlersChain{func(_ *Context) {}})
  279. router.GET("/path2", func(c *Context) {})
  280. w := performRequest(router, "POST", "/path2")
  281. assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
  282. }
  283. func TestRouteNotAllowedDisabled(t *testing.T) {
  284. router := New()
  285. router.HandleMethodNotAllowed = false
  286. router.POST("/path", func(c *Context) {})
  287. w := performRequest(router, "GET", "/path")
  288. assert.Equal(t, http.StatusNotFound, w.Code)
  289. router.NoMethod(func(c *Context) {
  290. c.String(http.StatusTeapot, "responseText")
  291. })
  292. w = performRequest(router, "GET", "/path")
  293. assert.Equal(t, "404 page not found", w.Body.String())
  294. assert.Equal(t, http.StatusNotFound, w.Code)
  295. }
  296. func TestRouterNotFound(t *testing.T) {
  297. router := New()
  298. router.RedirectFixedPath = true
  299. router.GET("/path", func(c *Context) {})
  300. router.GET("/dir/", func(c *Context) {})
  301. router.GET("/", func(c *Context) {})
  302. testRoutes := []struct {
  303. route string
  304. code int
  305. location string
  306. }{
  307. {"/path/", http.StatusMovedPermanently, "/path"}, // TSR -/
  308. {"/dir", http.StatusMovedPermanently, "/dir/"}, // TSR +/
  309. {"", http.StatusMovedPermanently, "/"}, // TSR +/
  310. {"/PATH", http.StatusMovedPermanently, "/path"}, // Fixed Case
  311. {"/DIR/", http.StatusMovedPermanently, "/dir/"}, // Fixed Case
  312. {"/PATH/", http.StatusMovedPermanently, "/path"}, // Fixed Case -/
  313. {"/DIR", http.StatusMovedPermanently, "/dir/"}, // Fixed Case +/
  314. {"/../path", http.StatusMovedPermanently, "/path"}, // CleanPath
  315. {"/nope", http.StatusNotFound, ""}, // NotFound
  316. }
  317. for _, tr := range testRoutes {
  318. w := performRequest(router, "GET", tr.route)
  319. assert.Equal(t, tr.code, w.Code)
  320. if w.Code != http.StatusNotFound {
  321. assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location")))
  322. }
  323. }
  324. // Test custom not found handler
  325. var notFound bool
  326. router.NoRoute(func(c *Context) {
  327. c.AbortWithStatus(http.StatusNotFound)
  328. notFound = true
  329. })
  330. w := performRequest(router, "GET", "/nope")
  331. assert.Equal(t, http.StatusNotFound, w.Code)
  332. assert.True(t, notFound)
  333. // Test other method than GET (want 307 instead of 301)
  334. router.PATCH("/path", func(c *Context) {})
  335. w = performRequest(router, "PATCH", "/path/")
  336. assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
  337. assert.Equal(t, "map[Location:[/path]]", fmt.Sprint(w.Header()))
  338. // Test special case where no node for the prefix "/" exists
  339. router = New()
  340. router.GET("/a", func(c *Context) {})
  341. w = performRequest(router, "GET", "/")
  342. assert.Equal(t, http.StatusNotFound, w.Code)
  343. }
  344. func TestRouterStaticFSNotFound(t *testing.T) {
  345. router := New()
  346. router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/")))
  347. router.NoRoute(func(c *Context) {
  348. c.String(404, "non existent")
  349. })
  350. w := performRequest(router, "GET", "/nonexistent")
  351. assert.Equal(t, "non existent", w.Body.String())
  352. w = performRequest(router, "HEAD", "/nonexistent")
  353. assert.Equal(t, "non existent", w.Body.String())
  354. }
  355. func TestRouteRawPath(t *testing.T) {
  356. route := New()
  357. route.UseRawPath = true
  358. route.POST("/project/:name/build/:num", func(c *Context) {
  359. name := c.Params.ByName("name")
  360. num := c.Params.ByName("num")
  361. assert.Equal(t, name, c.Param("name"))
  362. assert.Equal(t, num, c.Param("num"))
  363. assert.Equal(t, "Some/Other/Project", name)
  364. assert.Equal(t, "222", num)
  365. })
  366. w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/222")
  367. assert.Equal(t, http.StatusOK, w.Code)
  368. }
  369. func TestRouteRawPathNoUnescape(t *testing.T) {
  370. route := New()
  371. route.UseRawPath = true
  372. route.UnescapePathValues = false
  373. route.POST("/project/:name/build/:num", func(c *Context) {
  374. name := c.Params.ByName("name")
  375. num := c.Params.ByName("num")
  376. assert.Equal(t, name, c.Param("name"))
  377. assert.Equal(t, num, c.Param("num"))
  378. assert.Equal(t, "Some%2FOther%2FProject", name)
  379. assert.Equal(t, "333", num)
  380. })
  381. w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333")
  382. assert.Equal(t, http.StatusOK, w.Code)
  383. }
  384. func TestRouteServeErrorWithWriteHeader(t *testing.T) {
  385. route := New()
  386. route.Use(func(c *Context) {
  387. c.Status(421)
  388. c.Next()
  389. })
  390. w := performRequest(route, "GET", "/NotFound")
  391. assert.Equal(t, 421, w.Code)
  392. assert.Equal(t, 0, w.Body.Len())
  393. }