context_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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. "bytes"
  7. "errors"
  8. "html/template"
  9. "net/http"
  10. "net/http/httptest"
  11. "testing"
  12. "time"
  13. "github.com/gin-gonic/gin/binding"
  14. "github.com/manucorporat/sse"
  15. "github.com/stretchr/testify/assert"
  16. )
  17. // Unit tests TODO
  18. // func (c *Context) File(filepath string) {
  19. // func (c *Context) Negotiate(code int, config Negotiate) {
  20. // BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
  21. // test that information is not leaked when reusing Contexts (using the Pool)
  22. func createTestContext() (c *Context, w *httptest.ResponseRecorder, r *Engine) {
  23. w = httptest.NewRecorder()
  24. r = New()
  25. c = r.allocateContext()
  26. c.reset()
  27. c.writermem.reset(w)
  28. return
  29. }
  30. func TestContextReset(t *testing.T) {
  31. router := New()
  32. c := router.allocateContext()
  33. assert.Equal(t, c.engine, router)
  34. c.index = 2
  35. c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()}
  36. c.Params = Params{Param{}}
  37. c.Error(errors.New("test"), nil)
  38. c.Set("foo", "bar")
  39. c.reset()
  40. assert.False(t, c.IsAborted())
  41. assert.Nil(t, c.Keys)
  42. assert.Nil(t, c.Accepted)
  43. assert.Len(t, c.Errors, 0)
  44. assert.Empty(t, c.Errors.Errors())
  45. assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
  46. assert.Len(t, c.Params, 0)
  47. assert.Equal(t, c.index, -1)
  48. assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
  49. }
  50. // TestContextSetGet tests that a parameter is set correctly on the
  51. // current context and can be retrieved using Get.
  52. func TestContextSetGet(t *testing.T) {
  53. c, _, _ := createTestContext()
  54. c.Set("foo", "bar")
  55. value, err := c.Get("foo")
  56. assert.Equal(t, value, "bar")
  57. assert.True(t, err)
  58. value, err = c.Get("foo2")
  59. assert.Nil(t, value)
  60. assert.False(t, err)
  61. assert.Equal(t, c.MustGet("foo"), "bar")
  62. assert.Panics(t, func() { c.MustGet("no_exist") })
  63. }
  64. func TestContextSetGetValues(t *testing.T) {
  65. c, _, _ := createTestContext()
  66. c.Set("string", "this is a string")
  67. c.Set("int32", int32(-42))
  68. c.Set("int64", int64(42424242424242))
  69. c.Set("uint64", uint64(42))
  70. c.Set("float32", float32(4.2))
  71. c.Set("float64", 4.2)
  72. var a interface{} = 1
  73. c.Set("intInterface", a)
  74. assert.Exactly(t, c.MustGet("string").(string), "this is a string")
  75. assert.Exactly(t, c.MustGet("int32").(int32), int32(-42))
  76. assert.Exactly(t, c.MustGet("int64").(int64), int64(42424242424242))
  77. assert.Exactly(t, c.MustGet("uint64").(uint64), uint64(42))
  78. assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2))
  79. assert.Exactly(t, c.MustGet("float64").(float64), 4.2)
  80. assert.Exactly(t, c.MustGet("intInterface").(int), 1)
  81. }
  82. func TestContextCopy(t *testing.T) {
  83. c, _, _ := createTestContext()
  84. c.index = 2
  85. c.Request, _ = http.NewRequest("POST", "/hola", nil)
  86. c.handlers = HandlersChain{func(c *Context) {}}
  87. c.Params = Params{Param{Key: "foo", Value: "bar"}}
  88. c.Set("foo", "bar")
  89. cp := c.Copy()
  90. assert.Nil(t, cp.handlers)
  91. assert.Nil(t, cp.writermem.ResponseWriter)
  92. assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter))
  93. assert.Equal(t, cp.Request, c.Request)
  94. assert.Equal(t, cp.index, AbortIndex)
  95. assert.Equal(t, cp.Keys, c.Keys)
  96. assert.Equal(t, cp.engine, c.engine)
  97. assert.Equal(t, cp.Params, c.Params)
  98. }
  99. func TestContextFormParse(t *testing.T) {
  100. c, _, _ := createTestContext()
  101. c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10", nil)
  102. assert.Equal(t, c.DefaultFormValue("foo", "none"), "bar")
  103. assert.Equal(t, c.FormValue("foo"), "bar")
  104. assert.Empty(t, c.PostFormValue("foo"))
  105. assert.Equal(t, c.DefaultFormValue("page", "0"), "10")
  106. assert.Equal(t, c.FormValue("page"), "10")
  107. assert.Empty(t, c.PostFormValue("page"))
  108. assert.Equal(t, c.DefaultFormValue("NoKey", "nada"), "nada")
  109. assert.Empty(t, c.FormValue("NoKey"))
  110. assert.Empty(t, c.PostFormValue("NoKey"))
  111. }
  112. func TestContextPostFormParse(t *testing.T) {
  113. c, _, _ := createTestContext()
  114. body := bytes.NewBufferString("foo=bar&page=11&both=POST")
  115. c.Request, _ = http.NewRequest("POST", "/?both=GET&id=main", body)
  116. c.Request.Header.Add("Content-Type", MIMEPOSTForm)
  117. assert.Equal(t, c.DefaultPostFormValue("foo", "none"), "bar")
  118. assert.Equal(t, c.PostFormValue("foo"), "bar")
  119. assert.Equal(t, c.FormValue("foo"), "bar")
  120. assert.Equal(t, c.DefaultPostFormValue("page", "0"), "11")
  121. assert.Equal(t, c.PostFormValue("page"), "11")
  122. assert.Equal(t, c.FormValue("page"), "11")
  123. assert.Equal(t, c.PostFormValue("both"), "POST")
  124. assert.Equal(t, c.FormValue("both"), "POST")
  125. assert.Equal(t, c.FormValue("id"), "main")
  126. assert.Empty(t, c.PostFormValue("id"))
  127. assert.Equal(t, c.DefaultPostFormValue("NoKey", "nada"), "nada")
  128. assert.Empty(t, c.PostFormValue("NoKey"))
  129. assert.Empty(t, c.FormValue("NoKey"))
  130. }
  131. // Tests that the response is serialized as JSON
  132. // and Content-Type is set to application/json
  133. func TestContextRenderJSON(t *testing.T) {
  134. c, w, _ := createTestContext()
  135. c.JSON(201, H{"foo": "bar"})
  136. assert.Equal(t, w.Code, 201)
  137. assert.Equal(t, w.Body.String(), "{\"foo\":\"bar\"}\n")
  138. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8")
  139. }
  140. // Tests that the response is serialized as JSON
  141. // and Content-Type is set to application/json
  142. func TestContextRenderIndentedJSON(t *testing.T) {
  143. c, w, _ := createTestContext()
  144. c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
  145. assert.Equal(t, w.Code, 201)
  146. assert.Equal(t, w.Body.String(), "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}")
  147. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8")
  148. }
  149. // Tests that the response executes the templates
  150. // and responds with Content-Type set to text/html
  151. func TestContextRenderHTML(t *testing.T) {
  152. c, w, router := createTestContext()
  153. templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
  154. router.SetHTMLTemplate(templ)
  155. c.HTML(201, "t", H{"name": "alexandernyquist"})
  156. assert.Equal(t, w.Code, 201)
  157. assert.Equal(t, w.Body.String(), "Hello alexandernyquist")
  158. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8")
  159. }
  160. // TestContextXML tests that the response is serialized as XML
  161. // and Content-Type is set to application/xml
  162. func TestContextRenderXML(t *testing.T) {
  163. c, w, _ := createTestContext()
  164. c.XML(201, H{"foo": "bar"})
  165. assert.Equal(t, w.Code, 201)
  166. assert.Equal(t, w.Body.String(), "<map><foo>bar</foo></map>")
  167. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8")
  168. }
  169. // TestContextString tests that the response is returned
  170. // with Content-Type set to text/plain
  171. func TestContextRenderString(t *testing.T) {
  172. c, w, _ := createTestContext()
  173. c.String(201, "test %s %d", "string", 2)
  174. assert.Equal(t, w.Code, 201)
  175. assert.Equal(t, w.Body.String(), "test string 2")
  176. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
  177. }
  178. // TestContextString tests that the response is returned
  179. // with Content-Type set to text/html
  180. func TestContextRenderHTMLString(t *testing.T) {
  181. c, w, _ := createTestContext()
  182. c.Header("Content-Type", "text/html; charset=utf-8")
  183. c.String(201, "<html>%s %d</html>", "string", 3)
  184. assert.Equal(t, w.Code, 201)
  185. assert.Equal(t, w.Body.String(), "<html>string 3</html>")
  186. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8")
  187. }
  188. // TestContextData tests that the response can be written from `bytesting`
  189. // with specified MIME type
  190. func TestContextRenderData(t *testing.T) {
  191. c, w, _ := createTestContext()
  192. c.Data(201, "text/csv", []byte(`foo,bar`))
  193. assert.Equal(t, w.Code, 201)
  194. assert.Equal(t, w.Body.String(), "foo,bar")
  195. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/csv")
  196. }
  197. func TestContextRenderSSE(t *testing.T) {
  198. c, w, _ := createTestContext()
  199. c.SSEvent("float", 1.5)
  200. c.Render(-1, sse.Event{
  201. Id: "123",
  202. Data: "text",
  203. })
  204. c.SSEvent("chat", H{
  205. "foo": "bar",
  206. "bar": "foo",
  207. })
  208. assert.Equal(t, w.Body.String(), "event: float\ndata: 1.5\n\nid: 123\ndata: text\n\nevent: chat\ndata: {\"bar\":\"foo\",\"foo\":\"bar\"}\n\n")
  209. }
  210. func TestContextRenderFile(t *testing.T) {
  211. c, w, _ := createTestContext()
  212. c.Request, _ = http.NewRequest("GET", "/", nil)
  213. c.File("./gin.go")
  214. assert.Equal(t, w.Code, 200)
  215. assert.Contains(t, w.Body.String(), "func New() *Engine {")
  216. assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
  217. }
  218. func TestContextHeaders(t *testing.T) {
  219. c, _, _ := createTestContext()
  220. c.Header("Content-Type", "text/plain")
  221. c.Header("X-Custom", "value")
  222. assert.Equal(t, c.Writer.Header().Get("Content-Type"), "text/plain")
  223. assert.Equal(t, c.Writer.Header().Get("X-Custom"), "value")
  224. c.Header("Content-Type", "text/html")
  225. c.Header("X-Custom", "")
  226. assert.Equal(t, c.Writer.Header().Get("Content-Type"), "text/html")
  227. _, exist := c.Writer.Header()["X-Custom"]
  228. assert.False(t, exist)
  229. }
  230. // TODO
  231. func TestContextRenderRedirectWithRelativePath(t *testing.T) {
  232. c, w, _ := createTestContext()
  233. c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
  234. assert.Panics(t, func() { c.Redirect(299, "/new_path") })
  235. assert.Panics(t, func() { c.Redirect(309, "/new_path") })
  236. c.Redirect(302, "/path")
  237. c.Writer.WriteHeaderNow()
  238. assert.Equal(t, w.Code, 302)
  239. assert.Equal(t, w.Header().Get("Location"), "/path")
  240. }
  241. func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
  242. c, w, _ := createTestContext()
  243. c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
  244. c.Redirect(302, "http://google.com")
  245. c.Writer.WriteHeaderNow()
  246. assert.Equal(t, w.Code, 302)
  247. assert.Equal(t, w.Header().Get("Location"), "http://google.com")
  248. }
  249. func TestContextNegotiationFormat(t *testing.T) {
  250. c, _, _ := createTestContext()
  251. c.Request, _ = http.NewRequest("POST", "", nil)
  252. assert.Panics(t, func() { c.NegotiateFormat() })
  253. assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON)
  254. assert.Equal(t, c.NegotiateFormat(MIMEHTML, MIMEJSON), MIMEHTML)
  255. }
  256. func TestContextNegotiationFormatWithAccept(t *testing.T) {
  257. c, _, _ := createTestContext()
  258. c.Request, _ = http.NewRequest("POST", "/", nil)
  259. c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
  260. assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEXML)
  261. assert.Equal(t, c.NegotiateFormat(MIMEXML, MIMEHTML), MIMEHTML)
  262. assert.Equal(t, c.NegotiateFormat(MIMEJSON), "")
  263. }
  264. func TestContextNegotiationFormatCustum(t *testing.T) {
  265. c, _, _ := createTestContext()
  266. c.Request, _ = http.NewRequest("POST", "/", nil)
  267. c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
  268. c.Accepted = nil
  269. c.SetAccepted(MIMEJSON, MIMEXML)
  270. assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON)
  271. assert.Equal(t, c.NegotiateFormat(MIMEXML, MIMEHTML), MIMEXML)
  272. assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON)
  273. }
  274. // TestContextData tests that the response can be written from `bytesting`
  275. // with specified MIME type
  276. func TestContextAbortWithStatus(t *testing.T) {
  277. c, w, _ := createTestContext()
  278. c.index = 4
  279. c.AbortWithStatus(401)
  280. c.Writer.WriteHeaderNow()
  281. assert.Equal(t, c.index, AbortIndex)
  282. assert.Equal(t, c.Writer.Status(), 401)
  283. assert.Equal(t, w.Code, 401)
  284. assert.True(t, c.IsAborted())
  285. }
  286. func TestContextError(t *testing.T) {
  287. c, _, _ := createTestContext()
  288. assert.Nil(t, c.LastError())
  289. assert.Empty(t, c.Errors.String())
  290. c.Error(errors.New("first error"), "some data")
  291. assert.Equal(t, c.LastError().Error(), "first error")
  292. assert.Len(t, c.Errors, 1)
  293. assert.Equal(t, c.Errors.String(), "Error #01: first error\n Meta: some data\n")
  294. c.Error(errors.New("second error"), "some data 2")
  295. assert.Equal(t, c.LastError().Error(), "second error")
  296. assert.Len(t, c.Errors, 2)
  297. assert.Equal(t, c.Errors.String(), "Error #01: first error\n Meta: some data\n"+
  298. "Error #02: second error\n Meta: some data 2\n")
  299. assert.Equal(t, c.Errors[0].Error, errors.New("first error"))
  300. assert.Equal(t, c.Errors[0].Meta, "some data")
  301. assert.Equal(t, c.Errors[0].Flags, ErrorTypeExternal)
  302. assert.Equal(t, c.Errors[1].Error, errors.New("second error"))
  303. assert.Equal(t, c.Errors[1].Meta, "some data 2")
  304. assert.Equal(t, c.Errors[1].Flags, ErrorTypeExternal)
  305. }
  306. func TestContextTypedError(t *testing.T) {
  307. c, _, _ := createTestContext()
  308. c.ErrorTyped(errors.New("externo 0"), ErrorTypeExternal, nil)
  309. c.ErrorTyped(errors.New("externo 1"), ErrorTypeExternal, nil)
  310. c.ErrorTyped(errors.New("interno 0"), ErrorTypeInternal, nil)
  311. c.ErrorTyped(errors.New("externo 2"), ErrorTypeExternal, nil)
  312. c.ErrorTyped(errors.New("interno 1"), ErrorTypeInternal, nil)
  313. c.ErrorTyped(errors.New("interno 2"), ErrorTypeInternal, nil)
  314. for _, err := range c.Errors.ByType(ErrorTypeExternal) {
  315. assert.Equal(t, err.Flags, ErrorTypeExternal)
  316. }
  317. for _, err := range c.Errors.ByType(ErrorTypeInternal) {
  318. assert.Equal(t, err.Flags, ErrorTypeInternal)
  319. }
  320. assert.Equal(t, c.Errors.Errors(), []string{"externo 0", "externo 1", "interno 0", "externo 2", "interno 1", "interno 2"})
  321. }
  322. func TestContextFail(t *testing.T) {
  323. c, w, _ := createTestContext()
  324. c.Fail(401, errors.New("bad input"))
  325. c.Writer.WriteHeaderNow()
  326. assert.Equal(t, w.Code, 401)
  327. assert.Equal(t, c.LastError().Error(), "bad input")
  328. assert.Equal(t, c.index, AbortIndex)
  329. assert.True(t, c.IsAborted())
  330. }
  331. func TestContextClientIP(t *testing.T) {
  332. c, _, _ := createTestContext()
  333. c.Request, _ = http.NewRequest("POST", "/", nil)
  334. c.Request.Header.Set("X-Real-IP", "10.10.10.10")
  335. c.Request.Header.Set("X-Forwarded-For", "20.20.20.20 , 30.30.30.30")
  336. c.Request.RemoteAddr = "40.40.40.40"
  337. assert.Equal(t, c.ClientIP(), "10.10.10.10")
  338. c.Request.Header.Del("X-Real-IP")
  339. assert.Equal(t, c.ClientIP(), "20.20.20.20")
  340. c.Request.Header.Del("X-Forwarded-For")
  341. assert.Equal(t, c.ClientIP(), "40.40.40.40")
  342. }
  343. func TestContextContentType(t *testing.T) {
  344. c, _, _ := createTestContext()
  345. c.Request, _ = http.NewRequest("POST", "/", nil)
  346. c.Request.Header.Set("Content-Type", "application/json; charset=utf-8")
  347. assert.Equal(t, c.ContentType(), "application/json")
  348. }
  349. func TestContextAutoBind(t *testing.T) {
  350. c, w, _ := createTestContext()
  351. c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  352. c.Request.Header.Add("Content-Type", MIMEJSON)
  353. var obj struct {
  354. Foo string `json:"foo"`
  355. Bar string `json:"bar"`
  356. }
  357. assert.True(t, c.Bind(&obj))
  358. assert.Equal(t, obj.Bar, "foo")
  359. assert.Equal(t, obj.Foo, "bar")
  360. assert.Equal(t, w.Body.Len(), 0)
  361. }
  362. func TestContextBadAutoBind(t *testing.T) {
  363. c, w, _ := createTestContext()
  364. c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
  365. c.Request.Header.Add("Content-Type", MIMEJSON)
  366. var obj struct {
  367. Foo string `json:"foo"`
  368. Bar string `json:"bar"`
  369. }
  370. assert.False(t, c.IsAborted())
  371. assert.False(t, c.Bind(&obj))
  372. c.Writer.WriteHeaderNow()
  373. assert.Empty(t, obj.Bar)
  374. assert.Empty(t, obj.Foo)
  375. assert.Equal(t, w.Code, 400)
  376. assert.True(t, c.IsAborted())
  377. }
  378. func TestContextBindWith(t *testing.T) {
  379. c, w, _ := createTestContext()
  380. c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  381. c.Request.Header.Add("Content-Type", MIMEXML)
  382. var obj struct {
  383. Foo string `json:"foo"`
  384. Bar string `json:"bar"`
  385. }
  386. assert.True(t, c.BindWith(&obj, binding.JSON))
  387. assert.Equal(t, obj.Bar, "foo")
  388. assert.Equal(t, obj.Foo, "bar")
  389. assert.Equal(t, w.Body.Len(), 0)
  390. }
  391. func TestContextGolangContext(t *testing.T) {
  392. c, _, _ := createTestContext()
  393. c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  394. assert.NoError(t, c.Err())
  395. assert.Nil(t, c.Done())
  396. ti, ok := c.Deadline()
  397. assert.Equal(t, ti, time.Time{})
  398. assert.False(t, ok)
  399. assert.Equal(t, c.Value(0), c.Request)
  400. assert.Nil(t, c.Value("foo"))
  401. c.Set("foo", "bar")
  402. assert.Equal(t, c.Value("foo"), "bar")
  403. assert.Nil(t, c.Value(1))
  404. }