github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/courier/transport_http/transform/request_test.go (about) 1 package transform 2 3 import ( 4 "bytes" 5 "encoding/xml" 6 "fmt" 7 "io/ioutil" 8 "mime/multipart" 9 "net/http" 10 "reflect" 11 "strconv" 12 "strings" 13 "testing" 14 15 "github.com/julienschmidt/httprouter" 16 "github.com/stretchr/testify/assert" 17 18 "encoding/json" 19 20 "github.com/johnnyeven/libtools/courier/enumeration" 21 "github.com/johnnyeven/libtools/courier/status_error" 22 "github.com/johnnyeven/libtools/ptr" 23 ) 24 25 func MarshalParametersWithPath(req *http.Request, path string, v interface{}) error { 26 return MarshalParameters(ParameterGroupFromReflectValue(reflect.ValueOf(v)), &ParameterValuesGetterWithPath{ 27 ParameterValuesGetter: NewParameterValuesGetter(req), 28 Path: path, 29 }) 30 } 31 32 type ParameterValuesGetterWithPath struct { 33 *ParameterValuesGetter 34 Path string 35 params httprouter.Params 36 } 37 38 func (getter *ParameterValuesGetterWithPath) Initial() (err error) { 39 getter.params, err = GetParams(getter.Path, getter.Request.URL.Path) 40 if err != nil { 41 return 42 } 43 return getter.ParameterValuesGetter.Initial() 44 } 45 46 func GetParams(path string, url string) (params httprouter.Params, err error) { 47 pathArr := strings.Split(httprouter.CleanPath(path), "/") 48 urlArr := strings.Split(httprouter.CleanPath(url), "/") 49 50 if len(pathArr) != len(urlArr) { 51 return nil, fmt.Errorf("url %s is not match path %s", url, path) 52 } 53 54 for i, p := range pathArr { 55 if strings.HasPrefix(p, ":") { 56 params = append(params, httprouter.Param{ 57 Key: p[1:], 58 Value: urlArr[i], 59 }) 60 } 61 } 62 63 return params, nil 64 } 65 66 func (getter *ParameterValuesGetterWithPath) Param(name string) string { 67 return getter.params.ByName(name) 68 } 69 70 type MarshalInt int 71 72 func (marshalInt *MarshalInt) UnmarshalJSON(data []byte) error { 73 s, err := strconv.Unquote(string(data)) 74 if err != nil { 75 s = string(data) 76 } 77 i, err := strconv.ParseInt(s, 10, 64) 78 if err != nil { 79 return err 80 } 81 *marshalInt = MarshalInt(i) 82 return nil 83 } 84 85 func (marshalInt MarshalInt) MarshalJSON() ([]byte, error) { 86 return []byte(fmt.Sprintf(`"%d"`, marshalInt)), nil 87 } 88 89 type StructBody struct { 90 A int `json:"a" xml:"a"` 91 C uint `json:"c" xml:"c"` 92 D bool `json:"d" xml:"d"` 93 E string `json:"e" xml:"e"` 94 } 95 96 func TestTransToHttpReq(t *testing.T) { 97 tt := assert.New(t) 98 99 type CommonReq struct { 100 A int `name:"a" in:"header"` 101 B enumeration.Bool `name:"b" in:"header"` 102 C uint `name:"c" in:"header"` 103 D bool `name:"d" in:"header"` 104 } 105 106 type SuccessStruct struct { 107 CommonReq 108 E string `name:"e" in:"header"` 109 Q MarshalInt `name:"q" in:"header"` 110 111 F int `name:"f" in:"path"` 112 G uint64 `name:"g" in:"path" default:"0"` 113 H float64 `name:"h" in:"path"` 114 I *bool `name:"i" in:"path"` 115 J string `name:"j" in:"path"` 116 117 K int `name:"k" in:"query"` 118 L uint `name:"l" in:"query"` 119 N bool `name:"n" in:"query"` 120 O string `name:"o" in:"query"` 121 P MarshalInt `name:"p" in:"query"` 122 Ids []int `name:"ids" in:"query"` 123 Body StructBody `in:"body"` 124 } 125 126 ss := &SuccessStruct{ 127 CommonReq: CommonReq{ 128 A: -1, 129 B: enumeration.BOOL__FALSE, 130 C: 1, 131 D: true, 132 }, 133 E: "a", 134 Q: 11, 135 136 F: -1, 137 G: 1, 138 H: 1.1, 139 I: ptr.Bool(false), 140 J: "b", 141 142 K: -1, 143 L: 1, 144 N: true, 145 O: "c", 146 P: 11, 147 Ids: []int{1, 2}, 148 149 Body: StructBody{ 150 A: -1, 151 C: 1, 152 D: true, 153 E: "d", 154 }, 155 } 156 157 req, err := NewRequest("POST", "http://127.0.0.1/:f/:g/:h/:i/:j", ss) 158 tt.Nil(err) 159 160 tt.Equal("-1", req.Header.Get("a")) 161 tt.Equal("false", req.Header.Get("b")) 162 tt.Equal("1", req.Header.Get("c")) 163 tt.Equal("true", req.Header.Get("d")) 164 tt.Equal("a", req.Header.Get("e")) 165 tt.Equal("11", req.Header.Get("q")) 166 tt.Equal("ids=1%2C2&k=-1&l=1&n=true&o=c&p=11", req.URL.Query().Encode()) 167 tt.Equal("/-1/1/1.1/false/b", req.URL.Path) 168 169 bodyBytes, err := CloneRequestBody(req) 170 tt.NoError(err) 171 172 reqBody := &StructBody{} 173 json.Unmarshal(bodyBytes, reqBody) 174 tt.Equal(ss.Body, *reqBody) 175 176 { 177 ssForReceive := &SuccessStruct{} 178 err := MarshalParametersWithPath(req, "/:f/:g/:h/:i/:j", ssForReceive) 179 tt.NoError(err) 180 tt.Equal(ss, ssForReceive) 181 } 182 } 183 184 func TestTransToHttpReqWithXML(t *testing.T) { 185 tt := assert.New(t) 186 187 type SuccessStruct struct { 188 Body struct { 189 XMLName xml.Name `xml:"person" json:"-"` 190 StructBody 191 } `in:"body" fmt:"xml"` 192 } 193 194 ss := &SuccessStruct{} 195 ss.Body.StructBody = StructBody{ 196 A: -1, 197 C: 1, 198 D: true, 199 E: "d", 200 } 201 202 req, err := NewRequest("POST", "http://127.0.0.1", ss) 203 tt.Nil(err) 204 205 bodyBytes, err := CloneRequestBody(req) 206 tt.NoError(err) 207 208 reqBody := &struct { 209 XMLName xml.Name `xml:"person" json:"-"` 210 StructBody 211 }{} 212 213 xml.Unmarshal(bodyBytes, reqBody) 214 tt.Equal(ss.Body.StructBody, reqBody.StructBody) 215 } 216 217 func TestTransToHttpReqWithBodyError(t *testing.T) { 218 tt := assert.New(t) 219 type SuccessStruct struct { 220 Body StructBody `in:"body"` 221 } 222 223 ss := &SuccessStruct{ 224 Body: StructBody{ 225 A: -1, 226 C: 1, 227 D: true, 228 E: "d", 229 }, 230 } 231 232 req, err := NewRequest("POST", "http://127.0.0.1", ss) 233 tt.Nil(err) 234 235 bodyBytes, err := ioutil.ReadAll(req.Body) 236 tt.NoError(err) 237 req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes[0:9])) 238 239 reqBody := &StructBody{} 240 json.Unmarshal(bodyBytes, reqBody) 241 tt.Equal(ss.Body, *reqBody) 242 243 { 244 ssForReceive := &SuccessStruct{} 245 err := MarshalParametersWithPath(req, "/", ssForReceive) 246 tt.Error(err) 247 tt.Equal(int64(status_error.InvalidBodyStruct), status_error.FromError(err).Code) 248 } 249 } 250 251 func TestTransToHttpReqWithoutParams(t *testing.T) { 252 tt := assert.New(t) 253 req, err := NewRequest("POST", "http://127.0.0.1", nil) 254 tt.Nil(err) 255 tt.Equal("http://127.0.0.1/", req.URL.String()) 256 } 257 258 type FormData struct { 259 A []int `name:"a"` 260 C uint `name:"c" ` 261 } 262 263 func TestTransToHttpReqWithCookie(t *testing.T) { 264 tt := assert.New(t) 265 266 type Req struct { 267 Cookie string `in:"cookie" name:"cookie"` 268 Slice []string `in:"cookie" name:"slice"` 269 } 270 271 request := &Req{ 272 Cookie: "cookie", 273 Slice: []string{"1", "2"}, 274 } 275 276 req, err := NewRequest("POST", "http://127.0.0.1", request) 277 tt.Nil(err) 278 tt.NotNil(req.Header.Get("Cookie")) 279 280 { 281 requestForReceive := &Req{} 282 err := MarshalParametersWithPath(req, "/", requestForReceive) 283 tt.NoError(err) 284 tt.Equal(request, requestForReceive) 285 } 286 } 287 288 func TestTransToHttpReqWithFormData(t *testing.T) { 289 tt := assert.New(t) 290 291 type Req struct { 292 FormData `in:"formData"` 293 } 294 295 request := Req{ 296 FormData: FormData{ 297 A: []int{ 298 -1, 1, 299 }, 300 C: 1, 301 }, 302 } 303 304 req, err := NewRequest("POST", "http://127.0.0.1", request) 305 tt.Nil(err) 306 tt.Nil(req.ParseForm()) 307 308 tt.Equal([]string{"-1", "1"}, req.Form["a"]) 309 tt.Equal([]string{"1"}, req.Form["c"]) 310 } 311 312 type FormDataMultipart struct { 313 Bytes []byte `name:"bytes"` 314 A []int `name:"a"` 315 C uint `name:"c" ` 316 Data StructBody `name:"data"` 317 318 File *multipart.FileHeader `name:"file"` 319 Files []*multipart.FileHeader `name:"files"` 320 } 321 322 func TestTransToHttpReqWithMultipart(t *testing.T) { 323 tt := assert.New(t) 324 325 type Req struct { 326 FormDataMultipart `in:"formData,multipart"` 327 } 328 329 request := &Req{ 330 FormDataMultipart: FormDataMultipart{ 331 A: []int{-1, 1}, 332 C: 1, 333 Bytes: []byte("bytes"), 334 Data: StructBody{ 335 A: -1, 336 C: 1, 337 D: true, 338 E: "d", 339 }, 340 }, 341 } 342 343 fileHeader, err := NewFileHeader("file", "test.txt", []byte("test test")) 344 tt.NoError(err) 345 request.File = fileHeader 346 347 fileHeader0, err := NewFileHeader("files", "test0.txt", []byte("test0 test0")) 348 tt.NoError(err) 349 request.Files = append(request.Files, fileHeader0) 350 351 fileHeader1, err := NewFileHeader("files", "test1.txt", []byte("test1 test1")) 352 tt.NoError(err) 353 request.Files = append(request.Files, fileHeader1) 354 355 req, err := NewRequest("POST", "http://127.0.0.1", request) 356 tt.NoError(err) 357 358 { 359 requestForReceive := &Req{} 360 err := MarshalParametersWithPath(req, "/", requestForReceive) 361 tt.NoError(err) 362 tt.Equal(request, requestForReceive) 363 } 364 365 tt.Equal("1", req.FormValue("c")) 366 tt.Equal([]string{"-1", "1"}, req.Form["a"]) 367 tt.Equal("bytes", req.FormValue("bytes")) 368 tt.Equal(`{"a":-1,"c":1,"d":true,"e":"d"}`, req.FormValue("data")) 369 370 _, _, errForRead := req.FormFile("files") 371 tt.NoError(errForRead) 372 tt.Equal(fileHeader, req.MultipartForm.File["file"][0]) 373 tt.Equal([]*multipart.FileHeader{fileHeader0, fileHeader1}, req.MultipartForm.File["files"]) 374 } 375 376 func TestTransToHttpReqWithMultipartForReadFailed(t *testing.T) { 377 tt := assert.New(t) 378 379 { 380 type Req struct { 381 FormData struct { 382 File *multipart.FileHeader `name:"file"` 383 } `in:"formData,multipart"` 384 } 385 386 request := &Req{} 387 388 fileHeader, err := NewFileHeader("file", "test.txt", nil) 389 tt.NoError(err) 390 request.FormData.File = fileHeader 391 392 req, err := NewRequest("POST", "http://127.0.0.1", request) 393 tt.NoError(err) 394 395 // destroy body 396 bodyBytes, err := ioutil.ReadAll(req.Body) 397 tt.NoError(err) 398 req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes[0 : len(bodyBytes)-5])) 399 400 { 401 requestForReceive := &Req{} 402 err := MarshalParametersWithPath(req, "/", requestForReceive) 403 tt.Error(status_error.ReadFormFileFailed, status_error.FromError(err).Code) 404 } 405 } 406 407 { 408 type Req struct { 409 FormData struct { 410 Files []*multipart.FileHeader `name:"files"` 411 } `in:"formData,multipart"` 412 } 413 414 request := &Req{} 415 416 fileHeader, err := NewFileHeader("files", "test.txt", nil) 417 tt.NoError(err) 418 request.FormData.Files = append(request.FormData.Files, fileHeader) 419 420 req, err := NewRequest("POST", "http://127.0.0.1", request) 421 tt.NoError(err) 422 423 // destroy body 424 bodyBytes, err := ioutil.ReadAll(req.Body) 425 tt.NoError(err) 426 req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes[0 : len(bodyBytes)-5])) 427 428 { 429 requestForReceive := &Req{} 430 err := MarshalParametersWithPath(req, "/", requestForReceive) 431 tt.Error(err) 432 tt.Equal(int64(status_error.ReadFormFileFailed), status_error.FromError(err).Code) 433 } 434 } 435 }