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  }