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  }