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  }