github.com/shuguocloud/go-zero@v1.3.0/rest/httpx/requests_test.go (about) 1 package httpx 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/shuguocloud/go-zero/rest/pathvar" 12 ) 13 14 func TestParseForm(t *testing.T) { 15 var v struct { 16 Name string `form:"name"` 17 Age int `form:"age"` 18 Percent float64 `form:"percent,optional"` 19 } 20 21 r, err := http.NewRequest(http.MethodGet, "/a?name=hello&age=18&percent=3.4", nil) 22 assert.Nil(t, err) 23 assert.Nil(t, Parse(r, &v)) 24 assert.Equal(t, "hello", v.Name) 25 assert.Equal(t, 18, v.Age) 26 assert.Equal(t, 3.4, v.Percent) 27 } 28 29 func TestParseForm_Error(t *testing.T) { 30 var v struct { 31 Name string `form:"name"` 32 Age int `form:"age"` 33 } 34 35 r := httptest.NewRequest(http.MethodGet, "/a?name=hello;", nil) 36 assert.NotNil(t, ParseForm(r, &v)) 37 } 38 39 func TestParseHeader(t *testing.T) { 40 tests := []struct { 41 name string 42 value string 43 expect map[string]string 44 }{ 45 { 46 name: "empty", 47 value: "", 48 expect: map[string]string{}, 49 }, 50 { 51 name: "regular", 52 value: "key=value", 53 expect: map[string]string{"key": "value"}, 54 }, 55 { 56 name: "next empty", 57 value: "key=value;", 58 expect: map[string]string{"key": "value"}, 59 }, 60 { 61 name: "regular", 62 value: "key=value;foo", 63 expect: map[string]string{"key": "value"}, 64 }, 65 } 66 67 for _, test := range tests { 68 test := test 69 t.Run(test.name, func(t *testing.T) { 70 t.Parallel() 71 72 m := ParseHeader(test.value) 73 assert.EqualValues(t, test.expect, m) 74 }) 75 } 76 } 77 78 func TestParsePath(t *testing.T) { 79 var v struct { 80 Name string `path:"name"` 81 Age int `path:"age"` 82 } 83 84 r := httptest.NewRequest(http.MethodGet, "/", nil) 85 r = pathvar.WithVars(r, map[string]string{ 86 "name": "foo", 87 "age": "18", 88 }) 89 err := Parse(r, &v) 90 assert.Nil(t, err) 91 assert.Equal(t, "foo", v.Name) 92 assert.Equal(t, 18, v.Age) 93 } 94 95 func TestParsePath_Error(t *testing.T) { 96 var v struct { 97 Name string `path:"name"` 98 Age int `path:"age"` 99 } 100 101 r := httptest.NewRequest(http.MethodGet, "/", nil) 102 r = pathvar.WithVars(r, map[string]string{ 103 "name": "foo", 104 }) 105 assert.NotNil(t, Parse(r, &v)) 106 } 107 108 func TestParseFormOutOfRange(t *testing.T) { 109 var v struct { 110 Age int `form:"age,range=[10:20)"` 111 } 112 113 tests := []struct { 114 url string 115 pass bool 116 }{ 117 { 118 url: "/a?age=5", 119 pass: false, 120 }, 121 { 122 url: "/a?age=10", 123 pass: true, 124 }, 125 { 126 url: "/a?age=15", 127 pass: true, 128 }, 129 { 130 url: "/a?age=20", 131 pass: false, 132 }, 133 { 134 url: "/a?age=28", 135 pass: false, 136 }, 137 } 138 139 for _, test := range tests { 140 r, err := http.NewRequest(http.MethodGet, test.url, nil) 141 assert.Nil(t, err) 142 143 err = Parse(r, &v) 144 if test.pass { 145 assert.Nil(t, err) 146 } else { 147 assert.NotNil(t, err) 148 } 149 } 150 } 151 152 func TestParseMultipartForm(t *testing.T) { 153 var v struct { 154 Name string `form:"name"` 155 Age int `form:"age"` 156 } 157 158 body := strings.Replace(`----------------------------220477612388154780019383 159 Content-Disposition: form-data; name="name" 160 161 kevin 162 ----------------------------220477612388154780019383 163 Content-Disposition: form-data; name="age" 164 165 18 166 ----------------------------220477612388154780019383--`, "\n", "\r\n", -1) 167 168 r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) 169 r.Header.Set(ContentType, "multipart/form-data; boundary=--------------------------220477612388154780019383") 170 171 assert.Nil(t, Parse(r, &v)) 172 assert.Equal(t, "kevin", v.Name) 173 assert.Equal(t, 18, v.Age) 174 } 175 176 func TestParseMultipartFormWrongBoundary(t *testing.T) { 177 var v struct { 178 Name string `form:"name"` 179 Age int `form:"age"` 180 } 181 182 body := strings.Replace(`----------------------------22047761238815478001938 183 Content-Disposition: form-data; name="name" 184 185 kevin 186 ----------------------------22047761238815478001938 187 Content-Disposition: form-data; name="age" 188 189 18 190 ----------------------------22047761238815478001938--`, "\n", "\r\n", -1) 191 192 r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) 193 r.Header.Set(ContentType, "multipart/form-data; boundary=--------------------------220477612388154780019383") 194 195 assert.NotNil(t, Parse(r, &v)) 196 } 197 198 func TestParseJsonBody(t *testing.T) { 199 t.Run("has body", func(t *testing.T) { 200 var v struct { 201 Name string `json:"name"` 202 Age int `json:"age"` 203 } 204 205 body := `{"name":"kevin", "age": 18}` 206 r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) 207 r.Header.Set(ContentType, ApplicationJson) 208 209 assert.Nil(t, Parse(r, &v)) 210 assert.Equal(t, "kevin", v.Name) 211 assert.Equal(t, 18, v.Age) 212 }) 213 214 t.Run("hasn't body", func(t *testing.T) { 215 var v struct { 216 Name string `json:"name,optional"` 217 Age int `json:"age,optional"` 218 } 219 220 r := httptest.NewRequest(http.MethodGet, "/", nil) 221 assert.Nil(t, Parse(r, &v)) 222 assert.Equal(t, "", v.Name) 223 assert.Equal(t, 0, v.Age) 224 }) 225 } 226 227 func TestParseRequired(t *testing.T) { 228 v := struct { 229 Name string `form:"name"` 230 Percent float64 `form:"percent"` 231 }{} 232 233 r, err := http.NewRequest(http.MethodGet, "/a?name=hello", nil) 234 assert.Nil(t, err) 235 assert.NotNil(t, Parse(r, &v)) 236 } 237 238 func TestParseOptions(t *testing.T) { 239 v := struct { 240 Position int8 `form:"pos,options=1|2"` 241 }{} 242 243 r, err := http.NewRequest(http.MethodGet, "/a?pos=4", nil) 244 assert.Nil(t, err) 245 assert.NotNil(t, Parse(r, &v)) 246 } 247 248 func TestParseHeaders(t *testing.T) { 249 type AnonymousStruct struct { 250 XRealIP string `header:"x-real-ip"` 251 Accept string `header:"Accept,optional"` 252 } 253 v := struct { 254 Name string `header:"name,optional"` 255 Percent string `header:"percent"` 256 Addrs []string `header:"addrs"` 257 XForwardedFor string `header:"X-Forwarded-For,optional"` 258 AnonymousStruct 259 }{} 260 request, err := http.NewRequest("POST", "/", nil) 261 if err != nil { 262 t.Fatal(err) 263 } 264 request.Header.Set("name", "chenquan") 265 request.Header.Set("percent", "1") 266 request.Header.Add("addrs", "addr1") 267 request.Header.Add("addrs", "addr2") 268 request.Header.Add("X-Forwarded-For", "10.0.10.11") 269 request.Header.Add("x-real-ip", "10.0.11.10") 270 request.Header.Add("Accept", "application/json") 271 err = ParseHeaders(request, &v) 272 if err != nil { 273 t.Fatal(err) 274 } 275 assert.Equal(t, "chenquan", v.Name) 276 assert.Equal(t, "1", v.Percent) 277 assert.Equal(t, []string{"addr1", "addr2"}, v.Addrs) 278 assert.Equal(t, "10.0.10.11", v.XForwardedFor) 279 assert.Equal(t, "10.0.11.10", v.XRealIP) 280 assert.Equal(t, "application/json", v.Accept) 281 } 282 283 func TestParseHeaders_Error(t *testing.T) { 284 v := struct { 285 Name string `header:"name"` 286 Age int `header:"age"` 287 }{} 288 289 r := httptest.NewRequest("POST", "/", nil) 290 r.Header.Set("name", "foo") 291 assert.NotNil(t, Parse(r, &v)) 292 } 293 294 func BenchmarkParseRaw(b *testing.B) { 295 r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", nil) 296 if err != nil { 297 b.Fatal(err) 298 } 299 300 for i := 0; i < b.N; i++ { 301 v := struct { 302 Name string `form:"name"` 303 Age int `form:"age"` 304 Percent float64 `form:"percent,optional"` 305 }{} 306 307 v.Name = r.FormValue("name") 308 v.Age, err = strconv.Atoi(r.FormValue("age")) 309 if err != nil { 310 b.Fatal(err) 311 } 312 v.Percent, err = strconv.ParseFloat(r.FormValue("percent"), 64) 313 if err != nil { 314 b.Fatal(err) 315 } 316 } 317 } 318 319 func BenchmarkParseAuto(b *testing.B) { 320 r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", nil) 321 if err != nil { 322 b.Fatal(err) 323 } 324 325 for i := 0; i < b.N; i++ { 326 v := struct { 327 Name string `form:"name"` 328 Age int `form:"age"` 329 Percent float64 `form:"percent,optional"` 330 }{} 331 332 if err = Parse(r, &v); err != nil { 333 b.Fatal(err) 334 } 335 } 336 }