...

Source file src/github.com/go-chi/chi/tree_test.go

Documentation: github.com/go-chi/chi

     1  package chi
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net/http"
     7  	"testing"
     8  )
     9  
    10  func TestTree(t *testing.T) {
    11  	hStub := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    12  	hIndex := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    13  	hFavicon := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    14  	hArticleList := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    15  	hArticleNear := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    16  	hArticleShow := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    17  	hArticleShowRelated := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    18  	hArticleShowOpts := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    19  	hArticleSlug := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    20  	hArticleByUser := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    21  	hUserList := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    22  	hUserShow := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    23  	hAdminCatchall := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    24  	hAdminAppShow := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    25  	hAdminAppShowCatchall := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    26  	hUserProfile := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    27  	hUserSuper := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    28  	hUserAll := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    29  	hHubView1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    30  	hHubView2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    31  	hHubView3 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    32  
    33  	tr := &node{}
    34  
    35  	tr.InsertRoute(mGET, "/", hIndex)
    36  	tr.InsertRoute(mGET, "/favicon.ico", hFavicon)
    37  
    38  	tr.InsertRoute(mGET, "/pages/*", hStub)
    39  
    40  	tr.InsertRoute(mGET, "/article", hArticleList)
    41  	tr.InsertRoute(mGET, "/article/", hArticleList)
    42  
    43  	tr.InsertRoute(mGET, "/article/near", hArticleNear)
    44  	tr.InsertRoute(mGET, "/article/{id}", hStub)
    45  	tr.InsertRoute(mGET, "/article/{id}", hArticleShow)
    46  	tr.InsertRoute(mGET, "/article/{id}", hArticleShow) // duplicate will have no effect
    47  	tr.InsertRoute(mGET, "/article/@{user}", hArticleByUser)
    48  
    49  	tr.InsertRoute(mGET, "/article/{sup}/{opts}", hArticleShowOpts)
    50  	tr.InsertRoute(mGET, "/article/{id}/{opts}", hArticleShowOpts) // overwrite above route, latest wins
    51  
    52  	tr.InsertRoute(mGET, "/article/{iffd}/edit", hStub)
    53  	tr.InsertRoute(mGET, "/article/{id}//related", hArticleShowRelated)
    54  	tr.InsertRoute(mGET, "/article/slug/{month}/-/{day}/{year}", hArticleSlug)
    55  
    56  	tr.InsertRoute(mGET, "/admin/user", hUserList)
    57  	tr.InsertRoute(mGET, "/admin/user/", hStub) // will get replaced by next route
    58  	tr.InsertRoute(mGET, "/admin/user/", hUserList)
    59  
    60  	tr.InsertRoute(mGET, "/admin/user//{id}", hUserShow)
    61  	tr.InsertRoute(mGET, "/admin/user/{id}", hUserShow)
    62  
    63  	tr.InsertRoute(mGET, "/admin/apps/{id}", hAdminAppShow)
    64  	tr.InsertRoute(mGET, "/admin/apps/{id}/*", hAdminAppShowCatchall)
    65  
    66  	tr.InsertRoute(mGET, "/admin/*", hStub) // catchall segment will get replaced by next route
    67  	tr.InsertRoute(mGET, "/admin/*", hAdminCatchall)
    68  
    69  	tr.InsertRoute(mGET, "/users/{userID}/profile", hUserProfile)
    70  	tr.InsertRoute(mGET, "/users/super/*", hUserSuper)
    71  	tr.InsertRoute(mGET, "/users/*", hUserAll)
    72  
    73  	tr.InsertRoute(mGET, "/hubs/{hubID}/view", hHubView1)
    74  	tr.InsertRoute(mGET, "/hubs/{hubID}/view/*", hHubView2)
    75  	sr := NewRouter()
    76  	sr.Get("/users", hHubView3)
    77  	tr.InsertRoute(mGET, "/hubs/{hubID}/*", sr)
    78  	tr.InsertRoute(mGET, "/hubs/{hubID}/users", hHubView3)
    79  
    80  	tests := []struct {
    81  		r string       // input request path
    82  		h http.Handler // output matched handler
    83  		k []string     // output param keys
    84  		v []string     // output param values
    85  	}{
    86  		{r: "/", h: hIndex, k: []string{}, v: []string{}},
    87  		{r: "/favicon.ico", h: hFavicon, k: []string{}, v: []string{}},
    88  
    89  		{r: "/pages", h: nil, k: []string{}, v: []string{}},
    90  		{r: "/pages/", h: hStub, k: []string{"*"}, v: []string{""}},
    91  		{r: "/pages/yes", h: hStub, k: []string{"*"}, v: []string{"yes"}},
    92  
    93  		{r: "/article", h: hArticleList, k: []string{}, v: []string{}},
    94  		{r: "/article/", h: hArticleList, k: []string{}, v: []string{}},
    95  		{r: "/article/near", h: hArticleNear, k: []string{}, v: []string{}},
    96  		{r: "/article/neard", h: hArticleShow, k: []string{"id"}, v: []string{"neard"}},
    97  		{r: "/article/123", h: hArticleShow, k: []string{"id"}, v: []string{"123"}},
    98  		{r: "/article/123/456", h: hArticleShowOpts, k: []string{"id", "opts"}, v: []string{"123", "456"}},
    99  		{r: "/article/@peter", h: hArticleByUser, k: []string{"user"}, v: []string{"peter"}},
   100  		{r: "/article/22//related", h: hArticleShowRelated, k: []string{"id"}, v: []string{"22"}},
   101  		{r: "/article/111/edit", h: hStub, k: []string{"iffd"}, v: []string{"111"}},
   102  		{r: "/article/slug/sept/-/4/2015", h: hArticleSlug, k: []string{"month", "day", "year"}, v: []string{"sept", "4", "2015"}},
   103  		{r: "/article/:id", h: hArticleShow, k: []string{"id"}, v: []string{":id"}},
   104  
   105  		{r: "/admin/user", h: hUserList, k: []string{}, v: []string{}},
   106  		{r: "/admin/user/", h: hUserList, k: []string{}, v: []string{}},
   107  		{r: "/admin/user/1", h: hUserShow, k: []string{"id"}, v: []string{"1"}},
   108  		{r: "/admin/user//1", h: hUserShow, k: []string{"id"}, v: []string{"1"}},
   109  		{r: "/admin/hi", h: hAdminCatchall, k: []string{"*"}, v: []string{"hi"}},
   110  		{r: "/admin/lots/of/:fun", h: hAdminCatchall, k: []string{"*"}, v: []string{"lots/of/:fun"}},
   111  		{r: "/admin/apps/333", h: hAdminAppShow, k: []string{"id"}, v: []string{"333"}},
   112  		{r: "/admin/apps/333/woot", h: hAdminAppShowCatchall, k: []string{"id", "*"}, v: []string{"333", "woot"}},
   113  
   114  		{r: "/hubs/123/view", h: hHubView1, k: []string{"hubID"}, v: []string{"123"}},
   115  		{r: "/hubs/123/view/index.html", h: hHubView2, k: []string{"hubID", "*"}, v: []string{"123", "index.html"}},
   116  		{r: "/hubs/123/users", h: hHubView3, k: []string{"hubID"}, v: []string{"123"}},
   117  
   118  		{r: "/users/123/profile", h: hUserProfile, k: []string{"userID"}, v: []string{"123"}},
   119  		{r: "/users/super/123/okay/yes", h: hUserSuper, k: []string{"*"}, v: []string{"123/okay/yes"}},
   120  		{r: "/users/123/okay/yes", h: hUserAll, k: []string{"*"}, v: []string{"123/okay/yes"}},
   121  	}
   122  
   123  	// log.Println("~~~~~~~~~")
   124  	// log.Println("~~~~~~~~~")
   125  	// debugPrintTree(0, 0, tr, 0)
   126  	// log.Println("~~~~~~~~~")
   127  	// log.Println("~~~~~~~~~")
   128  
   129  	for i, tt := range tests {
   130  		rctx := NewRouteContext()
   131  
   132  		_, handlers, _ := tr.FindRoute(rctx, mGET, tt.r)
   133  
   134  		var handler http.Handler
   135  		if methodHandler, ok := handlers[mGET]; ok {
   136  			handler = methodHandler.handler
   137  		}
   138  
   139  		paramKeys := rctx.routeParams.Keys
   140  		paramValues := rctx.routeParams.Values
   141  
   142  		if fmt.Sprintf("%v", tt.h) != fmt.Sprintf("%v", handler) {
   143  			t.Errorf("input [%d]: find '%s' expecting handler:%v , got:%v", i, tt.r, tt.h, handler)
   144  		}
   145  		if !stringSliceEqual(tt.k, paramKeys) {
   146  			t.Errorf("input [%d]: find '%s' expecting paramKeys:(%d)%v , got:(%d)%v", i, tt.r, len(tt.k), tt.k, len(paramKeys), paramKeys)
   147  		}
   148  		if !stringSliceEqual(tt.v, paramValues) {
   149  			t.Errorf("input [%d]: find '%s' expecting paramValues:(%d)%v , got:(%d)%v", i, tt.r, len(tt.v), tt.v, len(paramValues), paramValues)
   150  		}
   151  	}
   152  }
   153  
   154  func TestTreeMoar(t *testing.T) {
   155  	hStub := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   156  	hStub1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   157  	hStub2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   158  	hStub3 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   159  	hStub4 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   160  	hStub5 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   161  	hStub6 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   162  	hStub7 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   163  	hStub8 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   164  	hStub9 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   165  	hStub10 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   166  	hStub11 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   167  	hStub12 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   168  	hStub13 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   169  	hStub14 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   170  	hStub15 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   171  	hStub16 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   172  
   173  	// TODO: panic if we see {id}{x} because we're missing a delimiter, its not possible.
   174  	// also {:id}* is not possible.
   175  
   176  	tr := &node{}
   177  
   178  	tr.InsertRoute(mGET, "/articlefun", hStub5)
   179  	tr.InsertRoute(mGET, "/articles/{id}", hStub)
   180  	tr.InsertRoute(mDELETE, "/articles/{slug}", hStub8)
   181  	tr.InsertRoute(mGET, "/articles/search", hStub1)
   182  	tr.InsertRoute(mGET, "/articles/{id}:delete", hStub8)
   183  	tr.InsertRoute(mGET, "/articles/{iidd}!sup", hStub4)
   184  	tr.InsertRoute(mGET, "/articles/{id}:{op}", hStub3)
   185  	tr.InsertRoute(mGET, "/articles/{id}:{op}", hStub2)                              // this route sets a new handler for the above route
   186  	tr.InsertRoute(mGET, "/articles/{slug:^[a-z]+}/posts", hStub)                    // up to tail '/' will only match if contents match the rex
   187  	tr.InsertRoute(mGET, "/articles/{id}/posts/{pid}", hStub6)                       // /articles/123/posts/1
   188  	tr.InsertRoute(mGET, "/articles/{id}/posts/{month}/{day}/{year}/{slug}", hStub7) // /articles/123/posts/09/04/1984/juice
   189  	tr.InsertRoute(mGET, "/articles/{id}.json", hStub10)
   190  	tr.InsertRoute(mGET, "/articles/{id}/data.json", hStub11)
   191  	tr.InsertRoute(mGET, "/articles/files/{file}.{ext}", hStub12)
   192  	tr.InsertRoute(mPUT, "/articles/me", hStub13)
   193  
   194  	// TODO: make a separate test case for this one..
   195  	// tr.InsertRoute(mGET, "/articles/{id}/{id}", hStub1)                              // panic expected, we're duplicating param keys
   196  
   197  	tr.InsertRoute(mGET, "/pages/*", hStub)
   198  	tr.InsertRoute(mGET, "/pages/*", hStub9)
   199  
   200  	tr.InsertRoute(mGET, "/users/{id}", hStub14)
   201  	tr.InsertRoute(mGET, "/users/{id}/settings/{key}", hStub15)
   202  	tr.InsertRoute(mGET, "/users/{id}/settings/*", hStub16)
   203  
   204  	tests := []struct {
   205  		m methodTyp    // input request http method
   206  		r string       // input request path
   207  		h http.Handler // output matched handler
   208  		k []string     // output param keys
   209  		v []string     // output param values
   210  	}{
   211  		{m: mGET, r: "/articles/search", h: hStub1, k: []string{}, v: []string{}},
   212  		{m: mGET, r: "/articlefun", h: hStub5, k: []string{}, v: []string{}},
   213  		{m: mGET, r: "/articles/123", h: hStub, k: []string{"id"}, v: []string{"123"}},
   214  		{m: mDELETE, r: "/articles/123mm", h: hStub8, k: []string{"slug"}, v: []string{"123mm"}},
   215  		{m: mGET, r: "/articles/789:delete", h: hStub8, k: []string{"id"}, v: []string{"789"}},
   216  		{m: mGET, r: "/articles/789!sup", h: hStub4, k: []string{"iidd"}, v: []string{"789"}},
   217  		{m: mGET, r: "/articles/123:sync", h: hStub2, k: []string{"id", "op"}, v: []string{"123", "sync"}},
   218  		{m: mGET, r: "/articles/456/posts/1", h: hStub6, k: []string{"id", "pid"}, v: []string{"456", "1"}},
   219  		{m: mGET, r: "/articles/456/posts/09/04/1984/juice", h: hStub7, k: []string{"id", "month", "day", "year", "slug"}, v: []string{"456", "09", "04", "1984", "juice"}},
   220  		{m: mGET, r: "/articles/456.json", h: hStub10, k: []string{"id"}, v: []string{"456"}},
   221  		{m: mGET, r: "/articles/456/data.json", h: hStub11, k: []string{"id"}, v: []string{"456"}},
   222  
   223  		{m: mGET, r: "/articles/files/file.zip", h: hStub12, k: []string{"file", "ext"}, v: []string{"file", "zip"}},
   224  		{m: mGET, r: "/articles/files/photos.tar.gz", h: hStub12, k: []string{"file", "ext"}, v: []string{"photos", "tar.gz"}},
   225  		{m: mGET, r: "/articles/files/photos.tar.gz", h: hStub12, k: []string{"file", "ext"}, v: []string{"photos", "tar.gz"}},
   226  
   227  		{m: mPUT, r: "/articles/me", h: hStub13, k: []string{}, v: []string{}},
   228  		{m: mGET, r: "/articles/me", h: hStub, k: []string{"id"}, v: []string{"me"}},
   229  		{m: mGET, r: "/pages", h: nil, k: []string{}, v: []string{}},
   230  		{m: mGET, r: "/pages/", h: hStub9, k: []string{"*"}, v: []string{""}},
   231  		{m: mGET, r: "/pages/yes", h: hStub9, k: []string{"*"}, v: []string{"yes"}},
   232  
   233  		{m: mGET, r: "/users/1", h: hStub14, k: []string{"id"}, v: []string{"1"}},
   234  		{m: mGET, r: "/users/", h: nil, k: []string{}, v: []string{}},
   235  		{m: mGET, r: "/users/2/settings/password", h: hStub15, k: []string{"id", "key"}, v: []string{"2", "password"}},
   236  		{m: mGET, r: "/users/2/settings/", h: hStub16, k: []string{"id", "*"}, v: []string{"2", ""}},
   237  	}
   238  
   239  	// log.Println("~~~~~~~~~")
   240  	// log.Println("~~~~~~~~~")
   241  	// debugPrintTree(0, 0, tr, 0)
   242  	// log.Println("~~~~~~~~~")
   243  	// log.Println("~~~~~~~~~")
   244  
   245  	for i, tt := range tests {
   246  		rctx := NewRouteContext()
   247  
   248  		_, handlers, _ := tr.FindRoute(rctx, tt.m, tt.r)
   249  
   250  		var handler http.Handler
   251  		if methodHandler, ok := handlers[tt.m]; ok {
   252  			handler = methodHandler.handler
   253  		}
   254  
   255  		paramKeys := rctx.routeParams.Keys
   256  		paramValues := rctx.routeParams.Values
   257  
   258  		if fmt.Sprintf("%v", tt.h) != fmt.Sprintf("%v", handler) {
   259  			t.Errorf("input [%d]: find '%s' expecting handler:%v , got:%v", i, tt.r, tt.h, handler)
   260  		}
   261  		if !stringSliceEqual(tt.k, paramKeys) {
   262  			t.Errorf("input [%d]: find '%s' expecting paramKeys:(%d)%v , got:(%d)%v", i, tt.r, len(tt.k), tt.k, len(paramKeys), paramKeys)
   263  		}
   264  		if !stringSliceEqual(tt.v, paramValues) {
   265  			t.Errorf("input [%d]: find '%s' expecting paramValues:(%d)%v , got:(%d)%v", i, tt.r, len(tt.v), tt.v, len(paramValues), paramValues)
   266  		}
   267  	}
   268  }
   269  
   270  func TestTreeRegexp(t *testing.T) {
   271  	hStub1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   272  	hStub2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   273  	hStub3 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   274  	hStub4 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   275  	hStub5 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   276  	hStub6 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   277  	hStub7 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   278  
   279  	tr := &node{}
   280  	tr.InsertRoute(mGET, "/articles/{rid:^[0-9]{5,6}}", hStub7)
   281  	tr.InsertRoute(mGET, "/articles/{zid:^0[0-9]+}", hStub3)
   282  	tr.InsertRoute(mGET, "/articles/{name:^@[a-z]+}/posts", hStub4)
   283  	tr.InsertRoute(mGET, "/articles/{op:^[0-9]+}/run", hStub5)
   284  	tr.InsertRoute(mGET, "/articles/{id:^[0-9]+}", hStub1)
   285  	tr.InsertRoute(mGET, "/articles/{id:^[1-9]+}-{aux}", hStub6)
   286  	tr.InsertRoute(mGET, "/articles/{slug}", hStub2)
   287  
   288  	// log.Println("~~~~~~~~~")
   289  	// log.Println("~~~~~~~~~")
   290  	// debugPrintTree(0, 0, tr, 0)
   291  	// log.Println("~~~~~~~~~")
   292  	// log.Println("~~~~~~~~~")
   293  
   294  	tests := []struct {
   295  		r string       // input request path
   296  		h http.Handler // output matched handler
   297  		k []string     // output param keys
   298  		v []string     // output param values
   299  	}{
   300  		{r: "/articles", h: nil, k: []string{}, v: []string{}},
   301  		{r: "/articles/12345", h: hStub7, k: []string{"rid"}, v: []string{"12345"}},
   302  		{r: "/articles/123", h: hStub1, k: []string{"id"}, v: []string{"123"}},
   303  		{r: "/articles/how-to-build-a-router", h: hStub2, k: []string{"slug"}, v: []string{"how-to-build-a-router"}},
   304  		{r: "/articles/0456", h: hStub3, k: []string{"zid"}, v: []string{"0456"}},
   305  		{r: "/articles/@pk/posts", h: hStub4, k: []string{"name"}, v: []string{"@pk"}},
   306  		{r: "/articles/1/run", h: hStub5, k: []string{"op"}, v: []string{"1"}},
   307  		{r: "/articles/1122", h: hStub1, k: []string{"id"}, v: []string{"1122"}},
   308  		{r: "/articles/1122-yes", h: hStub6, k: []string{"id", "aux"}, v: []string{"1122", "yes"}},
   309  	}
   310  
   311  	for i, tt := range tests {
   312  		rctx := NewRouteContext()
   313  
   314  		_, handlers, _ := tr.FindRoute(rctx, mGET, tt.r)
   315  
   316  		var handler http.Handler
   317  		if methodHandler, ok := handlers[mGET]; ok {
   318  			handler = methodHandler.handler
   319  		}
   320  
   321  		paramKeys := rctx.routeParams.Keys
   322  		paramValues := rctx.routeParams.Values
   323  
   324  		if fmt.Sprintf("%v", tt.h) != fmt.Sprintf("%v", handler) {
   325  			t.Errorf("input [%d]: find '%s' expecting handler:%v , got:%v", i, tt.r, tt.h, handler)
   326  		}
   327  		if !stringSliceEqual(tt.k, paramKeys) {
   328  			t.Errorf("input [%d]: find '%s' expecting paramKeys:(%d)%v , got:(%d)%v", i, tt.r, len(tt.k), tt.k, len(paramKeys), paramKeys)
   329  		}
   330  		if !stringSliceEqual(tt.v, paramValues) {
   331  			t.Errorf("input [%d]: find '%s' expecting paramValues:(%d)%v , got:(%d)%v", i, tt.r, len(tt.v), tt.v, len(paramValues), paramValues)
   332  		}
   333  	}
   334  }
   335  
   336  func TestTreeRegexpRecursive(t *testing.T) {
   337  	hStub1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   338  	hStub2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   339  
   340  	tr := &node{}
   341  	tr.InsertRoute(mGET, "/one/{firstId:[a-z0-9-]+}/{secondId:[a-z0-9-]+}/first", hStub1)
   342  	tr.InsertRoute(mGET, "/one/{firstId:[a-z0-9-_]+}/{secondId:[a-z0-9-_]+}/second", hStub2)
   343  
   344  	// log.Println("~~~~~~~~~")
   345  	// log.Println("~~~~~~~~~")
   346  	// debugPrintTree(0, 0, tr, 0)
   347  	// log.Println("~~~~~~~~~")
   348  	// log.Println("~~~~~~~~~")
   349  
   350  	tests := []struct {
   351  		r string       // input request path
   352  		h http.Handler // output matched handler
   353  		k []string     // output param keys
   354  		v []string     // output param values
   355  	}{
   356  		{r: "/one/hello/world/first", h: hStub1, k: []string{"firstId", "secondId"}, v: []string{"hello", "world"}},
   357  		{r: "/one/hi_there/ok/second", h: hStub2, k: []string{"firstId", "secondId"}, v: []string{"hi_there", "ok"}},
   358  		{r: "/one///first", h: nil, k: []string{}, v: []string{}},
   359  		{r: "/one/hi/123/second", h: hStub2, k: []string{"firstId", "secondId"}, v: []string{"hi", "123"}},
   360  	}
   361  
   362  	for i, tt := range tests {
   363  		rctx := NewRouteContext()
   364  
   365  		_, handlers, _ := tr.FindRoute(rctx, mGET, tt.r)
   366  
   367  		var handler http.Handler
   368  		if methodHandler, ok := handlers[mGET]; ok {
   369  			handler = methodHandler.handler
   370  		}
   371  
   372  		paramKeys := rctx.routeParams.Keys
   373  		paramValues := rctx.routeParams.Values
   374  
   375  		if fmt.Sprintf("%v", tt.h) != fmt.Sprintf("%v", handler) {
   376  			t.Errorf("input [%d]: find '%s' expecting handler:%v , got:%v", i, tt.r, tt.h, handler)
   377  		}
   378  		if !stringSliceEqual(tt.k, paramKeys) {
   379  			t.Errorf("input [%d]: find '%s' expecting paramKeys:(%d)%v , got:(%d)%v", i, tt.r, len(tt.k), tt.k, len(paramKeys), paramKeys)
   380  		}
   381  		if !stringSliceEqual(tt.v, paramValues) {
   382  			t.Errorf("input [%d]: find '%s' expecting paramValues:(%d)%v , got:(%d)%v", i, tt.r, len(tt.v), tt.v, len(paramValues), paramValues)
   383  		}
   384  	}
   385  }
   386  
   387  func TestTreeRegexMatchWholeParam(t *testing.T) {
   388  	hStub1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   389  
   390  	rctx := NewRouteContext()
   391  	tr := &node{}
   392  	tr.InsertRoute(mGET, "/{id:[0-9]+}", hStub1)
   393  
   394  	tests := []struct {
   395  		url             string
   396  		expectedHandler http.Handler
   397  	}{
   398  		{url: "/13", expectedHandler: hStub1},
   399  		{url: "/a13", expectedHandler: nil},
   400  		{url: "/13.jpg", expectedHandler: nil},
   401  		{url: "/a13.jpg", expectedHandler: nil},
   402  	}
   403  
   404  	for _, tc := range tests {
   405  		_, _, handler := tr.FindRoute(rctx, mGET, tc.url)
   406  		if fmt.Sprintf("%v", tc.expectedHandler) != fmt.Sprintf("%v", handler) {
   407  			t.Errorf("expecting handler:%v , got:%v", tc.expectedHandler, handler)
   408  		}
   409  	}
   410  }
   411  
   412  func TestTreeFindPattern(t *testing.T) {
   413  	hStub1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   414  	hStub2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   415  	hStub3 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   416  
   417  	tr := &node{}
   418  	tr.InsertRoute(mGET, "/pages/*", hStub1)
   419  	tr.InsertRoute(mGET, "/articles/{id}/*", hStub2)
   420  	tr.InsertRoute(mGET, "/articles/{slug}/{uid}/*", hStub3)
   421  
   422  	if tr.findPattern("/pages") != false {
   423  		t.Errorf("find /pages failed")
   424  	}
   425  	if tr.findPattern("/pages*") != false {
   426  		t.Errorf("find /pages* failed - should be nil")
   427  	}
   428  	if tr.findPattern("/pages/*") == false {
   429  		t.Errorf("find /pages/* failed")
   430  	}
   431  	if tr.findPattern("/articles/{id}/*") == false {
   432  		t.Errorf("find /articles/{id}/* failed")
   433  	}
   434  	if tr.findPattern("/articles/{something}/*") == false {
   435  		t.Errorf("find /articles/{something}/* failed")
   436  	}
   437  	if tr.findPattern("/articles/{slug}/{uid}/*") == false {
   438  		t.Errorf("find /articles/{slug}/{uid}/* failed")
   439  	}
   440  }
   441  
   442  func debugPrintTree(parent int, i int, n *node, label byte) bool {
   443  	numEdges := 0
   444  	for _, nds := range n.children {
   445  		numEdges += len(nds)
   446  	}
   447  
   448  	// if n.handlers != nil {
   449  	// 	log.Printf("[node %d parent:%d] typ:%d prefix:%s label:%s tail:%s numEdges:%d isLeaf:%v handler:%v pat:%s keys:%v\n", i, parent, n.typ, n.prefix, string(label), string(n.tail), numEdges, n.isLeaf(), n.handlers, n.pattern, n.paramKeys)
   450  	// } else {
   451  	// 	log.Printf("[node %d parent:%d] typ:%d prefix:%s label:%s tail:%s numEdges:%d isLeaf:%v pat:%s keys:%v\n", i, parent, n.typ, n.prefix, string(label), string(n.tail), numEdges, n.isLeaf(), n.pattern, n.paramKeys)
   452  	// }
   453  	if n.endpoints != nil {
   454  		log.Printf("[node %d parent:%d] typ:%d prefix:%s label:%s tail:%s numEdges:%d isLeaf:%v handler:%v\n", i, parent, n.typ, n.prefix, string(label), string(n.tail), numEdges, n.isLeaf(), n.endpoints)
   455  	} else {
   456  		log.Printf("[node %d parent:%d] typ:%d prefix:%s label:%s tail:%s numEdges:%d isLeaf:%v\n", i, parent, n.typ, n.prefix, string(label), string(n.tail), numEdges, n.isLeaf())
   457  	}
   458  	parent = i
   459  	for _, nds := range n.children {
   460  		for _, e := range nds {
   461  			i++
   462  			if debugPrintTree(parent, i, e, e.label) {
   463  				return true
   464  			}
   465  		}
   466  	}
   467  	return false
   468  }
   469  
   470  func stringSliceEqual(a, b []string) bool {
   471  	if len(a) != len(b) {
   472  		return false
   473  	}
   474  	for i := range a {
   475  		if b[i] != a[i] {
   476  			return false
   477  		}
   478  	}
   479  	return true
   480  }
   481  
   482  func BenchmarkTreeGet(b *testing.B) {
   483  	h1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   484  	h2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
   485  
   486  	tr := &node{}
   487  	tr.InsertRoute(mGET, "/", h1)
   488  	tr.InsertRoute(mGET, "/ping", h2)
   489  	tr.InsertRoute(mGET, "/pingall", h2)
   490  	tr.InsertRoute(mGET, "/ping/{id}", h2)
   491  	tr.InsertRoute(mGET, "/ping/{id}/woop", h2)
   492  	tr.InsertRoute(mGET, "/ping/{id}/{opt}", h2)
   493  	tr.InsertRoute(mGET, "/pinggggg", h2)
   494  	tr.InsertRoute(mGET, "/hello", h1)
   495  
   496  	mctx := NewRouteContext()
   497  
   498  	b.ReportAllocs()
   499  	b.ResetTimer()
   500  
   501  	for i := 0; i < b.N; i++ {
   502  		mctx.Reset()
   503  		tr.FindRoute(mctx, mGET, "/ping/123/456")
   504  	}
   505  }
   506  
   507  func TestWalker(t *testing.T) {
   508  	r := bigMux()
   509  
   510  	// Walk the muxBig router tree.
   511  	if err := Walk(r, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
   512  		t.Logf("%v %v", method, route)
   513  
   514  		return nil
   515  	}); err != nil {
   516  		t.Error(err)
   517  	}
   518  }
   519  

View as plain text