github.com/lingyao2333/mo-zero@v1.4.1/rest/router/patrouter_test.go (about)

     1  package router
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/lingyao2333/mo-zero/rest/httpx"
    13  	"github.com/lingyao2333/mo-zero/rest/internal/header"
    14  	"github.com/lingyao2333/mo-zero/rest/pathvar"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  const contentLength = "Content-Length"
    19  
    20  type mockedResponseWriter struct {
    21  	code int
    22  }
    23  
    24  func (m *mockedResponseWriter) Header() http.Header {
    25  	return http.Header{}
    26  }
    27  
    28  func (m *mockedResponseWriter) Write(p []byte) (int, error) {
    29  	return len(p), nil
    30  }
    31  
    32  func (m *mockedResponseWriter) WriteHeader(code int) {
    33  	m.code = code
    34  }
    35  
    36  func TestPatRouterHandleErrors(t *testing.T) {
    37  	tests := []struct {
    38  		method string
    39  		path   string
    40  		err    error
    41  	}{
    42  		{"FAKE", "", ErrInvalidMethod},
    43  		{"GET", "", ErrInvalidPath},
    44  	}
    45  
    46  	for _, test := range tests {
    47  		t.Run(test.method, func(t *testing.T) {
    48  			router := NewRouter()
    49  			err := router.Handle(test.method, test.path, nil)
    50  			assert.Equal(t, test.err, err)
    51  		})
    52  	}
    53  }
    54  
    55  func TestPatRouterNotFound(t *testing.T) {
    56  	var notFound bool
    57  	router := NewRouter()
    58  	router.SetNotFoundHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    59  		notFound = true
    60  	}))
    61  	err := router.Handle(http.MethodGet, "/a/b",
    62  		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
    63  	assert.Nil(t, err)
    64  	r, _ := http.NewRequest(http.MethodGet, "/b/c", nil)
    65  	w := new(mockedResponseWriter)
    66  	router.ServeHTTP(w, r)
    67  	assert.True(t, notFound)
    68  }
    69  
    70  func TestPatRouterNotAllowed(t *testing.T) {
    71  	var notAllowed bool
    72  	router := NewRouter()
    73  	router.SetNotAllowedHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    74  		notAllowed = true
    75  	}))
    76  	err := router.Handle(http.MethodGet, "/a/b",
    77  		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
    78  	assert.Nil(t, err)
    79  	r, _ := http.NewRequest(http.MethodPost, "/a/b", nil)
    80  	w := new(mockedResponseWriter)
    81  	router.ServeHTTP(w, r)
    82  	assert.True(t, notAllowed)
    83  }
    84  
    85  func TestPatRouter(t *testing.T) {
    86  	tests := []struct {
    87  		method string
    88  		path   string
    89  		expect bool
    90  		code   int
    91  		err    error
    92  	}{
    93  		// we don't explicitly set status code, framework will do it.
    94  		{http.MethodGet, "/a/b", true, 0, nil},
    95  		{http.MethodGet, "/a/b/", true, 0, nil},
    96  		{http.MethodGet, "/a/b?a=b", true, 0, nil},
    97  		{http.MethodGet, "/a/b/?a=b", true, 0, nil},
    98  		{http.MethodGet, "/a/b/c?a=b", true, 0, nil},
    99  		{http.MethodGet, "/b/d", false, http.StatusNotFound, nil},
   100  	}
   101  
   102  	for _, test := range tests {
   103  		t.Run(test.method+":"+test.path, func(t *testing.T) {
   104  			routed := false
   105  			router := NewRouter()
   106  			err := router.Handle(test.method, "/a/:b", http.HandlerFunc(
   107  				func(w http.ResponseWriter, r *http.Request) {
   108  					routed = true
   109  					assert.Equal(t, 1, len(pathvar.Vars(r)))
   110  				}))
   111  			assert.Nil(t, err)
   112  			err = router.Handle(test.method, "/a/b/c", http.HandlerFunc(
   113  				func(w http.ResponseWriter, r *http.Request) {
   114  					routed = true
   115  					assert.Nil(t, pathvar.Vars(r))
   116  				}))
   117  			assert.Nil(t, err)
   118  			err = router.Handle(test.method, "/b/c", http.HandlerFunc(
   119  				func(w http.ResponseWriter, r *http.Request) {
   120  					routed = true
   121  				}))
   122  			assert.Nil(t, err)
   123  
   124  			w := new(mockedResponseWriter)
   125  			r, _ := http.NewRequest(test.method, test.path, nil)
   126  			router.ServeHTTP(w, r)
   127  			assert.Equal(t, test.expect, routed)
   128  			assert.Equal(t, test.code, w.code)
   129  
   130  			if test.code == 0 {
   131  				r, _ = http.NewRequest(http.MethodPut, test.path, nil)
   132  				router.ServeHTTP(w, r)
   133  				assert.Equal(t, http.StatusMethodNotAllowed, w.code)
   134  			}
   135  		})
   136  	}
   137  }
   138  
   139  func TestParseSlice(t *testing.T) {
   140  	body := `names=%5B%22first%22%2C%22second%22%5D`
   141  	reader := strings.NewReader(body)
   142  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/", reader)
   143  	assert.Nil(t, err)
   144  	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   145  
   146  	rt := NewRouter()
   147  	err = rt.Handle(http.MethodPost, "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   148  		v := struct {
   149  			Names []string `form:"names"`
   150  		}{}
   151  
   152  		err = httpx.Parse(r, &v)
   153  		assert.Nil(t, err)
   154  		assert.Equal(t, 2, len(v.Names))
   155  		assert.Equal(t, "first", v.Names[0])
   156  		assert.Equal(t, "second", v.Names[1])
   157  	}))
   158  	assert.Nil(t, err)
   159  
   160  	rr := httptest.NewRecorder()
   161  	rt.ServeHTTP(rr, r)
   162  }
   163  
   164  func TestParseJsonPost(t *testing.T) {
   165  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000",
   166  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   167  	assert.Nil(t, err)
   168  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   169  
   170  	router := NewRouter()
   171  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(func(
   172  		w http.ResponseWriter, r *http.Request) {
   173  		v := struct {
   174  			Name     string `path:"name"`
   175  			Year     int    `path:"year"`
   176  			Nickname string `form:"nickname"`
   177  			Zipcode  int64  `form:"zipcode"`
   178  			Location string `json:"location"`
   179  			Time     int64  `json:"time"`
   180  		}{}
   181  
   182  		err = httpx.Parse(r, &v)
   183  		assert.Nil(t, err)
   184  		_, err = io.WriteString(w, fmt.Sprintf("%s:%d:%s:%d:%s:%d", v.Name, v.Year,
   185  			v.Nickname, v.Zipcode, v.Location, v.Time))
   186  		assert.Nil(t, err)
   187  	}))
   188  	assert.Nil(t, err)
   189  
   190  	rr := httptest.NewRecorder()
   191  	router.ServeHTTP(rr, r)
   192  
   193  	assert.Equal(t, "kevin:2017:whatever:200000:shanghai:20170912", rr.Body.String())
   194  }
   195  
   196  func TestParseJsonPostWithIntSlice(t *testing.T) {
   197  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017",
   198  		bytes.NewBufferString(`{"ages": [1, 2], "years": [3, 4]}`))
   199  	assert.Nil(t, err)
   200  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   201  
   202  	router := NewRouter()
   203  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(func(
   204  		w http.ResponseWriter, r *http.Request) {
   205  		v := struct {
   206  			Name  string  `path:"name"`
   207  			Year  int     `path:"year"`
   208  			Ages  []int   `json:"ages"`
   209  			Years []int64 `json:"years"`
   210  		}{}
   211  
   212  		err = httpx.Parse(r, &v)
   213  		assert.Nil(t, err)
   214  		assert.ElementsMatch(t, []int{1, 2}, v.Ages)
   215  		assert.ElementsMatch(t, []int64{3, 4}, v.Years)
   216  	}))
   217  	assert.Nil(t, err)
   218  
   219  	rr := httptest.NewRecorder()
   220  	router.ServeHTTP(rr, r)
   221  }
   222  
   223  func TestParseJsonPostError(t *testing.T) {
   224  	payload := `[{"abcd": "cdef"}]`
   225  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000",
   226  		bytes.NewBufferString(payload))
   227  	assert.Nil(t, err)
   228  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   229  
   230  	router := NewRouter()
   231  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   232  		func(w http.ResponseWriter, r *http.Request) {
   233  			v := struct {
   234  				Name     string `path:"name"`
   235  				Year     int    `path:"year"`
   236  				Nickname string `form:"nickname"`
   237  				Zipcode  int64  `form:"zipcode"`
   238  				Location string `json:"location"`
   239  				Time     int64  `json:"time"`
   240  			}{}
   241  
   242  			err = httpx.Parse(r, &v)
   243  			assert.NotNil(t, err)
   244  		}))
   245  	assert.Nil(t, err)
   246  
   247  	rr := httptest.NewRecorder()
   248  	router.ServeHTTP(rr, r)
   249  }
   250  
   251  func TestParseJsonPostInvalidRequest(t *testing.T) {
   252  	payload := `{"ages": ["cdef"]}`
   253  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/",
   254  		bytes.NewBufferString(payload))
   255  	assert.Nil(t, err)
   256  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   257  
   258  	router := NewRouter()
   259  	err = router.Handle(http.MethodPost, "/", http.HandlerFunc(
   260  		func(w http.ResponseWriter, r *http.Request) {
   261  			v := struct {
   262  				Ages []int `json:"ages"`
   263  			}{}
   264  
   265  			err = httpx.Parse(r, &v)
   266  			assert.NotNil(t, err)
   267  		}))
   268  	assert.Nil(t, err)
   269  
   270  	rr := httptest.NewRecorder()
   271  	router.ServeHTTP(rr, r)
   272  }
   273  
   274  func TestParseJsonPostRequired(t *testing.T) {
   275  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017",
   276  		bytes.NewBufferString(`{"location": "shanghai"`))
   277  	assert.Nil(t, err)
   278  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   279  
   280  	router := NewRouter()
   281  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   282  		func(w http.ResponseWriter, r *http.Request) {
   283  			v := struct {
   284  				Name     string `path:"name"`
   285  				Year     int    `path:"year"`
   286  				Location string `json:"location"`
   287  				Time     int64  `json:"time"`
   288  			}{}
   289  
   290  			err = httpx.Parse(r, &v)
   291  			assert.NotNil(t, err)
   292  		}))
   293  	assert.Nil(t, err)
   294  
   295  	rr := httptest.NewRecorder()
   296  	router.ServeHTTP(rr, r)
   297  }
   298  
   299  func TestParsePath(t *testing.T) {
   300  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017", nil)
   301  	assert.Nil(t, err)
   302  
   303  	router := NewRouter()
   304  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   305  		func(w http.ResponseWriter, r *http.Request) {
   306  			v := struct {
   307  				Name string `path:"name"`
   308  				Year int    `path:"year"`
   309  			}{}
   310  
   311  			err = httpx.Parse(r, &v)
   312  			assert.Nil(t, err)
   313  			_, err = io.WriteString(w, fmt.Sprintf("%s in %d", v.Name, v.Year))
   314  			assert.Nil(t, err)
   315  		}))
   316  	assert.Nil(t, err)
   317  
   318  	rr := httptest.NewRecorder()
   319  	router.ServeHTTP(rr, r)
   320  
   321  	assert.Equal(t, "kevin in 2017", rr.Body.String())
   322  }
   323  
   324  func TestParsePathRequired(t *testing.T) {
   325  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin", nil)
   326  	assert.Nil(t, err)
   327  
   328  	router := NewRouter()
   329  	err = router.Handle(http.MethodGet, "/:name/", http.HandlerFunc(
   330  		func(w http.ResponseWriter, r *http.Request) {
   331  			v := struct {
   332  				Name string `path:"name"`
   333  				Year int    `path:"year"`
   334  			}{}
   335  
   336  			err = httpx.Parse(r, &v)
   337  			assert.NotNil(t, err)
   338  		}))
   339  	assert.Nil(t, err)
   340  
   341  	rr := httptest.NewRecorder()
   342  	router.ServeHTTP(rr, r)
   343  }
   344  
   345  func TestParseQuery(t *testing.T) {
   346  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000", nil)
   347  	assert.Nil(t, err)
   348  
   349  	router := NewRouter()
   350  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   351  		func(w http.ResponseWriter, r *http.Request) {
   352  			v := struct {
   353  				Nickname string `form:"nickname"`
   354  				Zipcode  int64  `form:"zipcode"`
   355  			}{}
   356  
   357  			err = httpx.Parse(r, &v)
   358  			assert.Nil(t, err)
   359  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode))
   360  			assert.Nil(t, err)
   361  		}))
   362  	assert.Nil(t, err)
   363  
   364  	rr := httptest.NewRecorder()
   365  	router.ServeHTTP(rr, r)
   366  
   367  	assert.Equal(t, "whatever:200000", rr.Body.String())
   368  }
   369  
   370  func TestParseQueryRequired(t *testing.T) {
   371  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever", nil)
   372  	assert.Nil(t, err)
   373  
   374  	router := NewRouter()
   375  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   376  		v := struct {
   377  			Nickname string `form:"nickname"`
   378  			Zipcode  int64  `form:"zipcode"`
   379  		}{}
   380  
   381  		err = httpx.Parse(r, &v)
   382  		assert.NotNil(t, err)
   383  	}))
   384  	assert.Nil(t, err)
   385  
   386  	rr := httptest.NewRecorder()
   387  	router.ServeHTTP(rr, r)
   388  }
   389  
   390  func TestParseOptional(t *testing.T) {
   391  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
   392  	assert.Nil(t, err)
   393  
   394  	router := NewRouter()
   395  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   396  		func(w http.ResponseWriter, r *http.Request) {
   397  			v := struct {
   398  				Nickname string `form:"nickname"`
   399  				Zipcode  int64  `form:"zipcode,optional"`
   400  			}{}
   401  
   402  			err = httpx.Parse(r, &v)
   403  			assert.Nil(t, err)
   404  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode))
   405  			assert.Nil(t, err)
   406  		}))
   407  	assert.Nil(t, err)
   408  
   409  	rr := httptest.NewRecorder()
   410  	router.ServeHTTP(rr, r)
   411  
   412  	assert.Equal(t, "whatever:0", rr.Body.String())
   413  }
   414  
   415  func TestParseNestedInRequestEmpty(t *testing.T) {
   416  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017", bytes.NewBufferString("{}"))
   417  	assert.Nil(t, err)
   418  
   419  	type (
   420  		Request struct {
   421  			Name string `path:"name"`
   422  			Year int    `path:"year"`
   423  		}
   424  
   425  		Audio struct {
   426  			Volume int `json:"volume"`
   427  		}
   428  
   429  		WrappedRequest struct {
   430  			Request
   431  			Audio Audio `json:"audio,optional"`
   432  		}
   433  	)
   434  
   435  	router := NewRouter()
   436  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   437  		func(w http.ResponseWriter, r *http.Request) {
   438  			var v WrappedRequest
   439  			err = httpx.Parse(r, &v)
   440  			assert.Nil(t, err)
   441  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Name, v.Year))
   442  			assert.Nil(t, err)
   443  		}))
   444  	assert.Nil(t, err)
   445  
   446  	rr := httptest.NewRecorder()
   447  	router.ServeHTTP(rr, r)
   448  
   449  	assert.Equal(t, "kevin:2017", rr.Body.String())
   450  }
   451  
   452  func TestParsePtrInRequest(t *testing.T) {
   453  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017",
   454  		bytes.NewBufferString(`{"audio": {"volume": 100}}`))
   455  	assert.Nil(t, err)
   456  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   457  
   458  	type (
   459  		Request struct {
   460  			Name string `path:"name"`
   461  			Year int    `path:"year"`
   462  		}
   463  
   464  		Audio struct {
   465  			Volume int `json:"volume"`
   466  		}
   467  
   468  		WrappedRequest struct {
   469  			Request
   470  			Audio *Audio `json:"audio,optional"`
   471  		}
   472  	)
   473  
   474  	router := NewRouter()
   475  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   476  		func(w http.ResponseWriter, r *http.Request) {
   477  			var v WrappedRequest
   478  			err = httpx.Parse(r, &v)
   479  			assert.Nil(t, err)
   480  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d:%d", v.Name, v.Year, v.Audio.Volume))
   481  			assert.Nil(t, err)
   482  		}))
   483  	assert.Nil(t, err)
   484  
   485  	rr := httptest.NewRecorder()
   486  	router.ServeHTTP(rr, r)
   487  
   488  	assert.Equal(t, "kevin:2017:100", rr.Body.String())
   489  }
   490  
   491  func TestParsePtrInRequestEmpty(t *testing.T) {
   492  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin", bytes.NewBufferString("{}"))
   493  	assert.Nil(t, err)
   494  
   495  	type (
   496  		Audio struct {
   497  			Volume int `json:"volume"`
   498  		}
   499  
   500  		WrappedRequest struct {
   501  			Audio *Audio `json:"audio,optional"`
   502  		}
   503  	)
   504  
   505  	router := NewRouter()
   506  	err = router.Handle(http.MethodPost, "/kevin", http.HandlerFunc(
   507  		func(w http.ResponseWriter, r *http.Request) {
   508  			var v WrappedRequest
   509  			err = httpx.Parse(r, &v)
   510  			assert.Nil(t, err)
   511  		}))
   512  	assert.Nil(t, err)
   513  
   514  	rr := httptest.NewRecorder()
   515  	router.ServeHTTP(rr, r)
   516  }
   517  
   518  func TestParseQueryOptional(t *testing.T) {
   519  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
   520  	assert.Nil(t, err)
   521  
   522  	router := NewRouter()
   523  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   524  		func(w http.ResponseWriter, r *http.Request) {
   525  			v := struct {
   526  				Nickname string `form:"nickname"`
   527  				Zipcode  int64  `form:"zipcode,optional"`
   528  			}{}
   529  
   530  			err = httpx.Parse(r, &v)
   531  			assert.Nil(t, err)
   532  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode))
   533  			assert.Nil(t, err)
   534  		}))
   535  	assert.Nil(t, err)
   536  
   537  	rr := httptest.NewRecorder()
   538  	router.ServeHTTP(rr, r)
   539  
   540  	assert.Equal(t, "whatever:0", rr.Body.String())
   541  }
   542  
   543  func TestParse(t *testing.T) {
   544  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000", nil)
   545  	assert.Nil(t, err)
   546  
   547  	router := NewRouter()
   548  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   549  		func(w http.ResponseWriter, r *http.Request) {
   550  			v := struct {
   551  				Name     string `path:"name"`
   552  				Year     int    `path:"year"`
   553  				Nickname string `form:"nickname"`
   554  				Zipcode  int64  `form:"zipcode"`
   555  			}{}
   556  
   557  			err = httpx.Parse(r, &v)
   558  			assert.Nil(t, err)
   559  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d:%s:%d", v.Name, v.Year, v.Nickname, v.Zipcode))
   560  			assert.Nil(t, err)
   561  		}))
   562  	assert.Nil(t, err)
   563  
   564  	rr := httptest.NewRecorder()
   565  	router.ServeHTTP(rr, r)
   566  
   567  	assert.Equal(t, "kevin:2017:whatever:200000", rr.Body.String())
   568  }
   569  
   570  func TestParseWrappedRequest(t *testing.T) {
   571  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017", nil)
   572  	assert.Nil(t, err)
   573  
   574  	type (
   575  		Request struct {
   576  			Name string `path:"name"`
   577  			Year int    `path:"year"`
   578  		}
   579  
   580  		WrappedRequest struct {
   581  			Request
   582  		}
   583  	)
   584  
   585  	router := NewRouter()
   586  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   587  		func(w http.ResponseWriter, r *http.Request) {
   588  			var v WrappedRequest
   589  			err = httpx.Parse(r, &v)
   590  			assert.Nil(t, err)
   591  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Name, v.Year))
   592  		}))
   593  	assert.Nil(t, err)
   594  
   595  	rr := httptest.NewRecorder()
   596  	router.ServeHTTP(rr, r)
   597  
   598  	assert.Equal(t, "kevin:2017", rr.Body.String())
   599  }
   600  
   601  func TestParseWrappedGetRequestWithJsonHeader(t *testing.T) {
   602  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017", bytes.NewReader(nil))
   603  	assert.Nil(t, err)
   604  	r.Header.Set(httpx.ContentType, header.JsonContentType)
   605  
   606  	type (
   607  		Request struct {
   608  			Name string `path:"name"`
   609  			Year int    `path:"year"`
   610  		}
   611  
   612  		WrappedRequest struct {
   613  			Request
   614  		}
   615  	)
   616  
   617  	router := NewRouter()
   618  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   619  		func(w http.ResponseWriter, r *http.Request) {
   620  			var v WrappedRequest
   621  			err = httpx.Parse(r, &v)
   622  			assert.Nil(t, err)
   623  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Name, v.Year))
   624  			assert.Nil(t, err)
   625  		}))
   626  	assert.Nil(t, err)
   627  
   628  	rr := httptest.NewRecorder()
   629  	router.ServeHTTP(rr, r)
   630  
   631  	assert.Equal(t, "kevin:2017", rr.Body.String())
   632  }
   633  
   634  func TestParseWrappedHeadRequestWithJsonHeader(t *testing.T) {
   635  	r, err := http.NewRequest(http.MethodHead, "http://hello.com/kevin/2017", bytes.NewReader(nil))
   636  	assert.Nil(t, err)
   637  	r.Header.Set(httpx.ContentType, header.JsonContentType)
   638  
   639  	type (
   640  		Request struct {
   641  			Name string `path:"name"`
   642  			Year int    `path:"year"`
   643  		}
   644  
   645  		WrappedRequest struct {
   646  			Request
   647  		}
   648  	)
   649  
   650  	router := NewRouter()
   651  	err = router.Handle(http.MethodHead, "/:name/:year", http.HandlerFunc(
   652  		func(w http.ResponseWriter, r *http.Request) {
   653  			var v WrappedRequest
   654  			err = httpx.Parse(r, &v)
   655  			assert.Nil(t, err)
   656  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Name, v.Year))
   657  			assert.Nil(t, err)
   658  		}))
   659  	assert.Nil(t, err)
   660  
   661  	rr := httptest.NewRecorder()
   662  	router.ServeHTTP(rr, r)
   663  
   664  	assert.Equal(t, "kevin:2017", rr.Body.String())
   665  }
   666  
   667  func TestParseWrappedRequestPtr(t *testing.T) {
   668  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017", nil)
   669  	assert.Nil(t, err)
   670  
   671  	type (
   672  		Request struct {
   673  			Name string `path:"name"`
   674  			Year int    `path:"year"`
   675  		}
   676  
   677  		WrappedRequest struct {
   678  			*Request
   679  		}
   680  	)
   681  
   682  	router := NewRouter()
   683  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   684  		func(w http.ResponseWriter, r *http.Request) {
   685  			var v WrappedRequest
   686  			err = httpx.Parse(r, &v)
   687  			assert.Nil(t, err)
   688  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Name, v.Year))
   689  			assert.Nil(t, err)
   690  		}))
   691  	assert.Nil(t, err)
   692  
   693  	rr := httptest.NewRecorder()
   694  	router.ServeHTTP(rr, r)
   695  
   696  	assert.Equal(t, "kevin:2017", rr.Body.String())
   697  }
   698  
   699  func TestParseWithAll(t *testing.T) {
   700  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000",
   701  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   702  	assert.Nil(t, err)
   703  	r.Header.Set(httpx.ContentType, httpx.JsonContentType)
   704  
   705  	router := NewRouter()
   706  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   707  		v := struct {
   708  			Name     string `path:"name"`
   709  			Year     int    `path:"year"`
   710  			Nickname string `form:"nickname"`
   711  			Zipcode  int64  `form:"zipcode"`
   712  			Location string `json:"location"`
   713  			Time     int64  `json:"time"`
   714  		}{}
   715  
   716  		err = httpx.Parse(r, &v)
   717  		assert.Nil(t, err)
   718  		_, err = io.WriteString(w, fmt.Sprintf("%s:%d:%s:%d:%s:%d", v.Name, v.Year,
   719  			v.Nickname, v.Zipcode, v.Location, v.Time))
   720  		assert.Nil(t, err)
   721  	}))
   722  	assert.Nil(t, err)
   723  
   724  	rr := httptest.NewRecorder()
   725  	router.ServeHTTP(rr, r)
   726  
   727  	assert.Equal(t, "kevin:2017:whatever:200000:shanghai:20170912", rr.Body.String())
   728  }
   729  
   730  func TestParseWithAllUtf8(t *testing.T) {
   731  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000",
   732  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   733  	assert.Nil(t, err)
   734  	r.Header.Set(httpx.ContentType, header.JsonContentType)
   735  
   736  	router := NewRouter()
   737  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   738  		func(w http.ResponseWriter, r *http.Request) {
   739  			v := struct {
   740  				Name     string `path:"name"`
   741  				Year     int    `path:"year"`
   742  				Nickname string `form:"nickname"`
   743  				Zipcode  int64  `form:"zipcode"`
   744  				Location string `json:"location"`
   745  				Time     int64  `json:"time"`
   746  			}{}
   747  
   748  			err = httpx.Parse(r, &v)
   749  			assert.Nil(t, err)
   750  			_, err = io.WriteString(w, fmt.Sprintf("%s:%d:%s:%d:%s:%d", v.Name, v.Year,
   751  				v.Nickname, v.Zipcode, v.Location, v.Time))
   752  			assert.Nil(t, err)
   753  		}))
   754  	assert.Nil(t, err)
   755  
   756  	rr := httptest.NewRecorder()
   757  	router.ServeHTTP(rr, r)
   758  
   759  	assert.Equal(t, "kevin:2017:whatever:200000:shanghai:20170912", rr.Body.String())
   760  }
   761  
   762  func TestParseWithMissingForm(t *testing.T) {
   763  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever",
   764  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   765  	assert.Nil(t, err)
   766  
   767  	router := NewRouter()
   768  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   769  		func(w http.ResponseWriter, r *http.Request) {
   770  			v := struct {
   771  				Name     string `path:"name"`
   772  				Year     int    `path:"year"`
   773  				Nickname string `form:"nickname"`
   774  				Zipcode  int64  `form:"zipcode"`
   775  				Location string `json:"location"`
   776  				Time     int64  `json:"time"`
   777  			}{}
   778  
   779  			err = httpx.Parse(r, &v)
   780  			assert.NotNil(t, err)
   781  			assert.Equal(t, "field zipcode is not set", err.Error())
   782  		}))
   783  	assert.Nil(t, err)
   784  
   785  	rr := httptest.NewRecorder()
   786  	router.ServeHTTP(rr, r)
   787  }
   788  
   789  func TestParseWithMissingAllForms(t *testing.T) {
   790  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017",
   791  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   792  	assert.Nil(t, err)
   793  
   794  	router := NewRouter()
   795  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   796  		func(w http.ResponseWriter, r *http.Request) {
   797  			v := struct {
   798  				Name     string `path:"name"`
   799  				Year     int    `path:"year"`
   800  				Nickname string `form:"nickname"`
   801  				Zipcode  int64  `form:"zipcode"`
   802  				Location string `json:"location"`
   803  				Time     int64  `json:"time"`
   804  			}{}
   805  
   806  			err = httpx.Parse(r, &v)
   807  			assert.NotNil(t, err)
   808  		}))
   809  	assert.Nil(t, err)
   810  
   811  	rr := httptest.NewRecorder()
   812  	router.ServeHTTP(rr, r)
   813  }
   814  
   815  func TestParseWithMissingJson(t *testing.T) {
   816  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000",
   817  		bytes.NewBufferString(`{"location": "shanghai"}`))
   818  	assert.Nil(t, err)
   819  
   820  	router := NewRouter()
   821  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   822  		func(w http.ResponseWriter, r *http.Request) {
   823  			v := struct {
   824  				Name     string `path:"name"`
   825  				Year     int    `path:"year"`
   826  				Nickname string `form:"nickname"`
   827  				Zipcode  int64  `form:"zipcode"`
   828  				Location string `json:"location"`
   829  				Time     int64  `json:"time"`
   830  			}{}
   831  
   832  			err = httpx.Parse(r, &v)
   833  			assert.NotEqual(t, io.EOF, err)
   834  			assert.NotNil(t, httpx.Parse(r, &v))
   835  		}))
   836  	assert.Nil(t, err)
   837  
   838  	rr := httptest.NewRecorder()
   839  	router.ServeHTTP(rr, r)
   840  }
   841  
   842  func TestParseWithMissingAllJsons(t *testing.T) {
   843  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000", nil)
   844  	assert.Nil(t, err)
   845  
   846  	router := NewRouter()
   847  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   848  		func(w http.ResponseWriter, r *http.Request) {
   849  			v := struct {
   850  				Name     string `path:"name"`
   851  				Year     int    `path:"year"`
   852  				Nickname string `form:"nickname"`
   853  				Zipcode  int64  `form:"zipcode"`
   854  				Location string `json:"location"`
   855  				Time     int64  `json:"time"`
   856  			}{}
   857  
   858  			err = httpx.Parse(r, &v)
   859  			assert.NotEqual(t, io.EOF, err)
   860  			assert.NotNil(t, err)
   861  		}))
   862  	assert.Nil(t, err)
   863  
   864  	rr := httptest.NewRecorder()
   865  	router.ServeHTTP(rr, r)
   866  }
   867  
   868  func TestParseWithMissingPath(t *testing.T) {
   869  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/2017?nickname=whatever&zipcode=200000",
   870  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   871  	assert.Nil(t, err)
   872  
   873  	router := NewRouter()
   874  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   875  		func(w http.ResponseWriter, r *http.Request) {
   876  			v := struct {
   877  				Name     string `path:"name"`
   878  				Year     int    `path:"year"`
   879  				Nickname string `form:"nickname"`
   880  				Zipcode  int64  `form:"zipcode"`
   881  				Location string `json:"location"`
   882  				Time     int64  `json:"time"`
   883  			}{}
   884  
   885  			err = httpx.Parse(r, &v)
   886  			assert.NotNil(t, err)
   887  			assert.Equal(t, "field name is not set", err.Error())
   888  		}))
   889  	assert.Nil(t, err)
   890  
   891  	rr := httptest.NewRecorder()
   892  	router.ServeHTTP(rr, r)
   893  }
   894  
   895  func TestParseWithMissingAllPaths(t *testing.T) {
   896  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/?nickname=whatever&zipcode=200000",
   897  		bytes.NewBufferString(`{"location": "shanghai", "time": 20170912}`))
   898  	assert.Nil(t, err)
   899  
   900  	router := NewRouter()
   901  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   902  		func(w http.ResponseWriter, r *http.Request) {
   903  			v := struct {
   904  				Name     string `path:"name"`
   905  				Year     int    `path:"year"`
   906  				Nickname string `form:"nickname"`
   907  				Zipcode  int64  `form:"zipcode"`
   908  				Location string `json:"location"`
   909  				Time     int64  `json:"time"`
   910  			}{}
   911  
   912  			err = httpx.Parse(r, &v)
   913  			assert.NotNil(t, err)
   914  		}))
   915  	assert.Nil(t, err)
   916  
   917  	rr := httptest.NewRecorder()
   918  	router.ServeHTTP(rr, r)
   919  }
   920  
   921  func TestParseGetWithContentLengthHeader(t *testing.T) {
   922  	r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000", nil)
   923  	assert.Nil(t, err)
   924  	r.Header.Set(httpx.ContentType, header.JsonContentType)
   925  	r.Header.Set(contentLength, "1024")
   926  
   927  	router := NewRouter()
   928  	err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
   929  		func(w http.ResponseWriter, r *http.Request) {
   930  			v := struct {
   931  				Name     string `path:"name"`
   932  				Year     int    `path:"year"`
   933  				Nickname string `form:"nickname"`
   934  				Zipcode  int64  `form:"zipcode"`
   935  				Location string `json:"location"`
   936  				Time     int64  `json:"time"`
   937  			}{}
   938  
   939  			err = httpx.Parse(r, &v)
   940  			assert.NotNil(t, err)
   941  		}))
   942  	assert.Nil(t, err)
   943  
   944  	rr := httptest.NewRecorder()
   945  	router.ServeHTTP(rr, r)
   946  }
   947  
   948  func TestParseJsonPostWithTypeMismatch(t *testing.T) {
   949  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017?nickname=whatever&zipcode=200000",
   950  		bytes.NewBufferString(`{"time": "20170912"}`))
   951  	assert.Nil(t, err)
   952  	r.Header.Set(httpx.ContentType, header.JsonContentType)
   953  
   954  	router := NewRouter()
   955  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   956  		func(w http.ResponseWriter, r *http.Request) {
   957  			v := struct {
   958  				Name     string `path:"name"`
   959  				Year     int    `path:"year"`
   960  				Nickname string `form:"nickname"`
   961  				Zipcode  int64  `form:"zipcode"`
   962  				Time     int64  `json:"time"`
   963  			}{}
   964  
   965  			err = httpx.Parse(r, &v)
   966  			assert.NotNil(t, err)
   967  		}))
   968  	assert.Nil(t, err)
   969  
   970  	rr := httptest.NewRecorder()
   971  	router.ServeHTTP(rr, r)
   972  }
   973  
   974  func TestParseJsonPostWithInt2String(t *testing.T) {
   975  	r, err := http.NewRequest(http.MethodPost, "http://hello.com/kevin/2017",
   976  		bytes.NewBufferString(`{"time": 20170912}`))
   977  	assert.Nil(t, err)
   978  	r.Header.Set(httpx.ContentType, header.JsonContentType)
   979  
   980  	router := NewRouter()
   981  	err = router.Handle(http.MethodPost, "/:name/:year", http.HandlerFunc(
   982  		func(w http.ResponseWriter, r *http.Request) {
   983  			v := struct {
   984  				Name string `path:"name"`
   985  				Year int    `path:"year"`
   986  				Time string `json:"time"`
   987  			}{}
   988  
   989  			err = httpx.Parse(r, &v)
   990  			assert.NotNil(t, err)
   991  		}))
   992  	assert.Nil(t, err)
   993  
   994  	rr := httptest.NewRecorder()
   995  	router.ServeHTTP(rr, r)
   996  }
   997  
   998  func BenchmarkPatRouter(b *testing.B) {
   999  	b.ReportAllocs()
  1000  
  1001  	router := NewRouter()
  1002  	router.Handle(http.MethodGet, "/api/:user/:name", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1003  	}))
  1004  	w := &mockedResponseWriter{}
  1005  	r, _ := http.NewRequest(http.MethodGet, "/api/a/b", nil)
  1006  	for i := 0; i < b.N; i++ {
  1007  		router.ServeHTTP(w, r)
  1008  	}
  1009  }