binding_test.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. package binding
  2. import (
  3. "bytes"
  4. "mime/multipart"
  5. "net/http"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. )
  9. type FooStruct struct {
  10. Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" validate:"required"`
  11. }
  12. type FooBarStruct struct {
  13. FooStruct
  14. Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" validate:"required"`
  15. Slice []string `form:"slice" validate:"max=10"`
  16. }
  17. type ComplexDefaultStruct struct {
  18. Int int `form:"int" default:"999"`
  19. String string `form:"string" default:"default-string"`
  20. Bool bool `form:"bool" default:"false"`
  21. Int64Slice []int64 `form:"int64_slice,split" default:"1,2,3,4"`
  22. Int8Slice []int8 `form:"int8_slice,split" default:"1,2,3,4"`
  23. }
  24. type Int8SliceStruct struct {
  25. State []int8 `form:"state,split"`
  26. }
  27. type Int64SliceStruct struct {
  28. State []int64 `form:"state,split"`
  29. }
  30. type StringSliceStruct struct {
  31. State []string `form:"state,split"`
  32. }
  33. func TestBindingDefault(t *testing.T) {
  34. assert.Equal(t, Default("GET", ""), Form)
  35. assert.Equal(t, Default("GET", MIMEJSON), Form)
  36. assert.Equal(t, Default("GET", MIMEJSON+"; charset=utf-8"), Form)
  37. assert.Equal(t, Default("POST", MIMEJSON), JSON)
  38. assert.Equal(t, Default("PUT", MIMEJSON), JSON)
  39. assert.Equal(t, Default("POST", MIMEJSON+"; charset=utf-8"), JSON)
  40. assert.Equal(t, Default("PUT", MIMEJSON+"; charset=utf-8"), JSON)
  41. assert.Equal(t, Default("POST", MIMEXML), XML)
  42. assert.Equal(t, Default("PUT", MIMEXML2), XML)
  43. assert.Equal(t, Default("POST", MIMEPOSTForm), Form)
  44. assert.Equal(t, Default("PUT", MIMEPOSTForm), Form)
  45. assert.Equal(t, Default("POST", MIMEPOSTForm+"; charset=utf-8"), Form)
  46. assert.Equal(t, Default("PUT", MIMEPOSTForm+"; charset=utf-8"), Form)
  47. assert.Equal(t, Default("POST", MIMEMultipartPOSTForm), Form)
  48. assert.Equal(t, Default("PUT", MIMEMultipartPOSTForm), Form)
  49. }
  50. func TestStripContentType(t *testing.T) {
  51. c1 := "application/vnd.mozilla.xul+xml"
  52. c2 := "application/vnd.mozilla.xul+xml; charset=utf-8"
  53. assert.Equal(t, stripContentTypeParam(c1), c1)
  54. assert.Equal(t, stripContentTypeParam(c2), "application/vnd.mozilla.xul+xml")
  55. }
  56. func TestBindInt8Form(t *testing.T) {
  57. params := "state=1,2,3"
  58. req, _ := http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  59. q := new(Int8SliceStruct)
  60. Form.Bind(req, q)
  61. assert.EqualValues(t, []int8{1, 2, 3}, q.State)
  62. params = "state=1,2,3,256"
  63. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  64. q = new(Int8SliceStruct)
  65. assert.Error(t, Form.Bind(req, q))
  66. params = "state="
  67. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  68. q = new(Int8SliceStruct)
  69. assert.NoError(t, Form.Bind(req, q))
  70. assert.Len(t, q.State, 0)
  71. params = "state=1,,2"
  72. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  73. q = new(Int8SliceStruct)
  74. assert.NoError(t, Form.Bind(req, q))
  75. assert.EqualValues(t, []int8{1, 2}, q.State)
  76. }
  77. func TestBindInt64Form(t *testing.T) {
  78. params := "state=1,2,3"
  79. req, _ := http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  80. q := new(Int64SliceStruct)
  81. Form.Bind(req, q)
  82. assert.EqualValues(t, []int64{1, 2, 3}, q.State)
  83. params = "state="
  84. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  85. q = new(Int64SliceStruct)
  86. assert.NoError(t, Form.Bind(req, q))
  87. assert.Len(t, q.State, 0)
  88. }
  89. func TestBindStringForm(t *testing.T) {
  90. params := "state=1,2,3"
  91. req, _ := http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  92. q := new(StringSliceStruct)
  93. Form.Bind(req, q)
  94. assert.EqualValues(t, []string{"1", "2", "3"}, q.State)
  95. params = "state="
  96. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  97. q = new(StringSliceStruct)
  98. assert.NoError(t, Form.Bind(req, q))
  99. assert.Len(t, q.State, 0)
  100. params = "state=p,,p"
  101. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  102. q = new(StringSliceStruct)
  103. Form.Bind(req, q)
  104. assert.EqualValues(t, []string{"p", "p"}, q.State)
  105. }
  106. func TestBindingJSON(t *testing.T) {
  107. testBodyBinding(t,
  108. JSON, "json",
  109. "/", "/",
  110. `{"foo": "bar"}`, `{"bar": "foo"}`)
  111. }
  112. func TestBindingForm(t *testing.T) {
  113. testFormBinding(t, "POST",
  114. "/", "/",
  115. "foo=bar&bar=foo&slice=a&slice=b", "bar2=foo")
  116. }
  117. func TestBindingForm2(t *testing.T) {
  118. testFormBinding(t, "GET",
  119. "/?foo=bar&bar=foo", "/?bar2=foo",
  120. "", "")
  121. }
  122. func TestBindingQuery(t *testing.T) {
  123. testQueryBinding(t, "POST",
  124. "/?foo=bar&bar=foo", "/",
  125. "foo=unused", "bar2=foo")
  126. }
  127. func TestBindingQuery2(t *testing.T) {
  128. testQueryBinding(t, "GET",
  129. "/?foo=bar&bar=foo", "/?bar2=foo",
  130. "foo=unused", "")
  131. }
  132. func TestBindingXML(t *testing.T) {
  133. testBodyBinding(t,
  134. XML, "xml",
  135. "/", "/",
  136. "<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
  137. }
  138. func createFormPostRequest() *http.Request {
  139. req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
  140. req.Header.Set("Content-Type", MIMEPOSTForm)
  141. return req
  142. }
  143. func createFormMultipartRequest() *http.Request {
  144. boundary := "--testboundary"
  145. body := new(bytes.Buffer)
  146. mw := multipart.NewWriter(body)
  147. defer mw.Close()
  148. mw.SetBoundary(boundary)
  149. mw.WriteField("foo", "bar")
  150. mw.WriteField("bar", "foo")
  151. req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
  152. req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
  153. return req
  154. }
  155. func TestBindingFormPost(t *testing.T) {
  156. req := createFormPostRequest()
  157. var obj FooBarStruct
  158. FormPost.Bind(req, &obj)
  159. assert.Equal(t, obj.Foo, "bar")
  160. assert.Equal(t, obj.Bar, "foo")
  161. }
  162. func TestBindingFormMultipart(t *testing.T) {
  163. req := createFormMultipartRequest()
  164. var obj FooBarStruct
  165. FormMultipart.Bind(req, &obj)
  166. assert.Equal(t, obj.Foo, "bar")
  167. assert.Equal(t, obj.Bar, "foo")
  168. }
  169. func TestValidationFails(t *testing.T) {
  170. var obj FooStruct
  171. req := requestWithBody("POST", "/", `{"bar": "foo"}`)
  172. err := JSON.Bind(req, &obj)
  173. assert.Error(t, err)
  174. }
  175. func TestValidationDisabled(t *testing.T) {
  176. backup := Validator
  177. Validator = nil
  178. defer func() { Validator = backup }()
  179. var obj FooStruct
  180. req := requestWithBody("POST", "/", `{"bar": "foo"}`)
  181. err := JSON.Bind(req, &obj)
  182. assert.NoError(t, err)
  183. }
  184. func TestExistsSucceeds(t *testing.T) {
  185. type HogeStruct struct {
  186. Hoge *int `json:"hoge" binding:"exists"`
  187. }
  188. var obj HogeStruct
  189. req := requestWithBody("POST", "/", `{"hoge": 0}`)
  190. err := JSON.Bind(req, &obj)
  191. assert.NoError(t, err)
  192. }
  193. func TestFormDefaultValue(t *testing.T) {
  194. params := "int=333&string=hello&bool=true&int64_slice=5,6,7,8&int8_slice=5,6,7,8"
  195. req, _ := http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  196. q := new(ComplexDefaultStruct)
  197. assert.NoError(t, Form.Bind(req, q))
  198. assert.Equal(t, 333, q.Int)
  199. assert.Equal(t, "hello", q.String)
  200. assert.Equal(t, true, q.Bool)
  201. assert.EqualValues(t, []int64{5, 6, 7, 8}, q.Int64Slice)
  202. assert.EqualValues(t, []int8{5, 6, 7, 8}, q.Int8Slice)
  203. params = "string=hello&bool=false"
  204. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  205. q = new(ComplexDefaultStruct)
  206. assert.NoError(t, Form.Bind(req, q))
  207. assert.Equal(t, 999, q.Int)
  208. assert.Equal(t, "hello", q.String)
  209. assert.Equal(t, false, q.Bool)
  210. assert.EqualValues(t, []int64{1, 2, 3, 4}, q.Int64Slice)
  211. assert.EqualValues(t, []int8{1, 2, 3, 4}, q.Int8Slice)
  212. params = "strings=hello"
  213. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  214. q = new(ComplexDefaultStruct)
  215. assert.NoError(t, Form.Bind(req, q))
  216. assert.Equal(t, 999, q.Int)
  217. assert.Equal(t, "default-string", q.String)
  218. assert.Equal(t, false, q.Bool)
  219. assert.EqualValues(t, []int64{1, 2, 3, 4}, q.Int64Slice)
  220. assert.EqualValues(t, []int8{1, 2, 3, 4}, q.Int8Slice)
  221. params = "int=&string=&bool=true&int64_slice=&int8_slice="
  222. req, _ = http.NewRequest("GET", "http://api.bilibili.com/test?"+params, nil)
  223. q = new(ComplexDefaultStruct)
  224. assert.NoError(t, Form.Bind(req, q))
  225. assert.Equal(t, 999, q.Int)
  226. assert.Equal(t, "default-string", q.String)
  227. assert.Equal(t, true, q.Bool)
  228. assert.EqualValues(t, []int64{1, 2, 3, 4}, q.Int64Slice)
  229. assert.EqualValues(t, []int8{1, 2, 3, 4}, q.Int8Slice)
  230. }
  231. func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) {
  232. b := Form
  233. assert.Equal(t, b.Name(), "form")
  234. obj := FooBarStruct{}
  235. req := requestWithBody(method, path, body)
  236. if method == "POST" {
  237. req.Header.Add("Content-Type", MIMEPOSTForm)
  238. }
  239. err := b.Bind(req, &obj)
  240. assert.NoError(t, err)
  241. assert.Equal(t, obj.Foo, "bar")
  242. assert.Equal(t, obj.Bar, "foo")
  243. obj = FooBarStruct{}
  244. req = requestWithBody(method, badPath, badBody)
  245. err = JSON.Bind(req, &obj)
  246. assert.Error(t, err)
  247. }
  248. func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) {
  249. b := Query
  250. assert.Equal(t, b.Name(), "query")
  251. obj := FooBarStruct{}
  252. req := requestWithBody(method, path, body)
  253. if method == "POST" {
  254. req.Header.Add("Content-Type", MIMEPOSTForm)
  255. }
  256. err := b.Bind(req, &obj)
  257. assert.NoError(t, err)
  258. assert.Equal(t, obj.Foo, "bar")
  259. assert.Equal(t, obj.Bar, "foo")
  260. }
  261. func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
  262. assert.Equal(t, b.Name(), name)
  263. obj := FooStruct{}
  264. req := requestWithBody("POST", path, body)
  265. err := b.Bind(req, &obj)
  266. assert.NoError(t, err)
  267. assert.Equal(t, obj.Foo, "bar")
  268. obj = FooStruct{}
  269. req = requestWithBody("POST", badPath, badBody)
  270. err = JSON.Bind(req, &obj)
  271. assert.Error(t, err)
  272. }
  273. func requestWithBody(method, path, body string) (req *http.Request) {
  274. req, _ = http.NewRequest(method, path, bytes.NewBufferString(body))
  275. return
  276. }
  277. func BenchmarkBindingForm(b *testing.B) {
  278. req := requestWithBody("POST", "/", "foo=bar&bar=foo&slice=a&slice=b&slice=c&slice=w")
  279. req.Header.Add("Content-Type", MIMEPOSTForm)
  280. f := Form
  281. for i := 0; i < b.N; i++ {
  282. obj := FooBarStruct{}
  283. f.Bind(req, &obj)
  284. }
  285. }