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