...

Source file src/github.com/go-openapi/runtime/middleware/denco/router_test.go

Documentation: github.com/go-openapi/runtime/middleware/denco

     1  package denco_test
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/go-openapi/runtime/middleware/denco"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func routes() []denco.Record {
    15  	return []denco.Record{
    16  		{"/", "testroute0"},
    17  		{"/path/to/route", "testroute1"},
    18  		{"/path/to/other", "testroute2"},
    19  		{"/path/to/route/a", "testroute3"},
    20  		{"/path/to/:param", "testroute4"},
    21  		{"/gists/:param1/foo/:param2", "testroute12"},
    22  		{"/gists/:param1/foo/bar", "testroute11"},
    23  		{"/:param1/:param2/foo/:param3", "testroute13"},
    24  		{"/path/to/wildcard/*routepath", "testroute5"},
    25  		{"/path/to/:param1/:param2", "testroute6"},
    26  		{"/path/to/:param1/sep/:param2", "testroute7"},
    27  		{"/:year/:month/:day", "testroute8"},
    28  		{"/user/:id", "testroute9"},
    29  		{"/a/to/b/:param/*routepath", "testroute10"},
    30  		{"/path/with/key=:value", "testroute14"},
    31  	}
    32  }
    33  
    34  var realURIs = []denco.Record{
    35  	{"/authorizations", "/authorizations"},
    36  	{"/authorizations/:id", "/authorizations/:id"},
    37  	{"/applications/:client_id/tokens/:access_token", "/applications/:client_id/tokens/:access_token"},
    38  	{"/events", "/events"},
    39  	{"/repos/:owner/:repo/events", "/repos/:owner/:repo/events"},
    40  	{"/networks/:owner/:repo/events", "/networks/:owner/:repo/events"},
    41  	{"/orgs/:org/events", "/orgs/:org/events"},
    42  	{"/users/:user/received_events", "/users/:user/received_events"},
    43  	{"/users/:user/received_events/public", "/users/:user/received_events/public"},
    44  	{"/users/:user/events", "/users/:user/events"},
    45  	{"/users/:user/events/public", "/users/:user/events/public"},
    46  	{"/users/:user/events/orgs/:org", "/users/:user/events/orgs/:org"},
    47  	{"/feeds", "/feeds"},
    48  	{"/notifications", "/notifications"},
    49  	{"/repos/:owner/:repo/notifications", "/repos/:owner/:repo/notifications"},
    50  	{"/notifications/threads/:id", "/notifications/threads/:id"},
    51  	{"/notifications/threads/:id/subscription", "/notifications/threads/:id/subscription"},
    52  	{"/repos/:owner/:repo/stargazers", "/repos/:owner/:repo/stargazers"},
    53  	{"/users/:user/starred", "/users/:user/starred"},
    54  	{"/user/starred", "/user/starred"},
    55  	{"/user/starred/:owner/:repo", "/user/starred/:owner/:repo"},
    56  	{"/repos/:owner/:repo/subscribers", "/repos/:owner/:repo/subscribers"},
    57  	{"/users/:user/subscriptions", "/users/:user/subscriptions"},
    58  	{"/user/subscriptions", "/user/subscriptions"},
    59  	{"/repos/:owner/:repo/subscription", "/repos/:owner/:repo/subscription"},
    60  	{"/user/subscriptions/:owner/:repo", "/user/subscriptions/:owner/:repo"},
    61  	{"/users/:user/gists", "/users/:user/gists"},
    62  	{"/gists", "/gists"},
    63  	{"/gists/:id", "/gists/:id"},
    64  	{"/gists/:id/star", "/gists/:id/star"},
    65  	{"/repos/:owner/:repo/git/blobs/:sha", "/repos/:owner/:repo/git/blobs/:sha"},
    66  	{"/repos/:owner/:repo/git/commits/:sha", "/repos/:owner/:repo/git/commits/:sha"},
    67  	{"/repos/:owner/:repo/git/refs", "/repos/:owner/:repo/git/refs"},
    68  	{"/repos/:owner/:repo/git/tags/:sha", "/repos/:owner/:repo/git/tags/:sha"},
    69  	{"/repos/:owner/:repo/git/trees/:sha", "/repos/:owner/:repo/git/trees/:sha"},
    70  	{"/issues", "/issues"},
    71  	{"/user/issues", "/user/issues"},
    72  	{"/orgs/:org/issues", "/orgs/:org/issues"},
    73  	{"/repos/:owner/:repo/issues", "/repos/:owner/:repo/issues"},
    74  	{"/repos/:owner/:repo/issues/:number", "/repos/:owner/:repo/issues/:number"},
    75  	{"/repos/:owner/:repo/assignees", "/repos/:owner/:repo/assignees"},
    76  	{"/repos/:owner/:repo/assignees/:assignee", "/repos/:owner/:repo/assignees/:assignee"},
    77  	{"/repos/:owner/:repo/issues/:number/comments", "/repos/:owner/:repo/issues/:number/comments"},
    78  	{"/repos/:owner/:repo/issues/:number/events", "/repos/:owner/:repo/issues/:number/events"},
    79  	{"/repos/:owner/:repo/labels", "/repos/:owner/:repo/labels"},
    80  	{"/repos/:owner/:repo/labels/:name", "/repos/:owner/:repo/labels/:name"},
    81  	{"/repos/:owner/:repo/issues/:number/labels", "/repos/:owner/:repo/issues/:number/labels"},
    82  	{"/repos/:owner/:repo/milestones/:number/labels", "/repos/:owner/:repo/milestones/:number/labels"},
    83  	{"/repos/:owner/:repo/milestones", "/repos/:owner/:repo/milestones"},
    84  	{"/repos/:owner/:repo/milestones/:number", "/repos/:owner/:repo/milestones/:number"},
    85  	{"/emojis", "/emojis"},
    86  	{"/gitignore/templates", "/gitignore/templates"},
    87  	{"/gitignore/templates/:name", "/gitignore/templates/:name"},
    88  	{"/meta", "/meta"},
    89  	{"/rate_limit", "/rate_limit"},
    90  	{"/users/:user/orgs", "/users/:user/orgs"},
    91  	{"/user/orgs", "/user/orgs"},
    92  	{"/orgs/:org", "/orgs/:org"},
    93  	{"/orgs/:org/members", "/orgs/:org/members"},
    94  	{"/orgs/:org/members/:user", "/orgs/:org/members/:user"},
    95  	{"/orgs/:org/public_members", "/orgs/:org/public_members"},
    96  	{"/orgs/:org/public_members/:user", "/orgs/:org/public_members/:user"},
    97  	{"/orgs/:org/teams", "/orgs/:org/teams"},
    98  	{"/teams/:id", "/teams/:id"},
    99  	{"/teams/:id/members", "/teams/:id/members"},
   100  	{"/teams/:id/members/:user", "/teams/:id/members/:user"},
   101  	{"/teams/:id/repos", "/teams/:id/repos"},
   102  	{"/teams/:id/repos/:owner/:repo", "/teams/:id/repos/:owner/:repo"},
   103  	{"/user/teams", "/user/teams"},
   104  	{"/repos/:owner/:repo/pulls", "/repos/:owner/:repo/pulls"},
   105  	{"/repos/:owner/:repo/pulls/:number", "/repos/:owner/:repo/pulls/:number"},
   106  	{"/repos/:owner/:repo/pulls/:number/commits", "/repos/:owner/:repo/pulls/:number/commits"},
   107  	{"/repos/:owner/:repo/pulls/:number/files", "/repos/:owner/:repo/pulls/:number/files"},
   108  	{"/repos/:owner/:repo/pulls/:number/merge", "/repos/:owner/:repo/pulls/:number/merge"},
   109  	{"/repos/:owner/:repo/pulls/:number/comments", "/repos/:owner/:repo/pulls/:number/comments"},
   110  	{"/user/repos", "/user/repos"},
   111  	{"/users/:user/repos", "/users/:user/repos"},
   112  	{"/orgs/:org/repos", "/orgs/:org/repos"},
   113  	{"/repositories", "/repositories"},
   114  	{"/repos/:owner/:repo", "/repos/:owner/:repo"},
   115  	{"/repos/:owner/:repo/contributors", "/repos/:owner/:repo/contributors"},
   116  	{"/repos/:owner/:repo/languages", "/repos/:owner/:repo/languages"},
   117  	{"/repos/:owner/:repo/teams", "/repos/:owner/:repo/teams"},
   118  	{"/repos/:owner/:repo/tags", "/repos/:owner/:repo/tags"},
   119  	{"/repos/:owner/:repo/branches", "/repos/:owner/:repo/branches"},
   120  	{"/repos/:owner/:repo/branches/:branch", "/repos/:owner/:repo/branches/:branch"},
   121  	{"/repos/:owner/:repo/collaborators", "/repos/:owner/:repo/collaborators"},
   122  	{"/repos/:owner/:repo/collaborators/:user", "/repos/:owner/:repo/collaborators/:user"},
   123  	{"/repos/:owner/:repo/comments", "/repos/:owner/:repo/comments"},
   124  	{"/repos/:owner/:repo/commits/:sha/comments", "/repos/:owner/:repo/commits/:sha/comments"},
   125  	{"/repos/:owner/:repo/comments/:id", "/repos/:owner/:repo/comments/:id"},
   126  	{"/repos/:owner/:repo/commits", "/repos/:owner/:repo/commits"},
   127  	{"/repos/:owner/:repo/commits/:sha", "/repos/:owner/:repo/commits/:sha"},
   128  	{"/repos/:owner/:repo/readme", "/repos/:owner/:repo/readme"},
   129  	{"/repos/:owner/:repo/keys", "/repos/:owner/:repo/keys"},
   130  	{"/repos/:owner/:repo/keys/:id", "/repos/:owner/:repo/keys/:id"},
   131  	{"/repos/:owner/:repo/downloads", "/repos/:owner/:repo/downloads"},
   132  	{"/repos/:owner/:repo/downloads/:id", "/repos/:owner/:repo/downloads/:id"},
   133  	{"/repos/:owner/:repo/forks", "/repos/:owner/:repo/forks"},
   134  	{"/repos/:owner/:repo/hooks", "/repos/:owner/:repo/hooks"},
   135  	{"/repos/:owner/:repo/hooks/:id", "/repos/:owner/:repo/hooks/:id"},
   136  	{"/repos/:owner/:repo/releases", "/repos/:owner/:repo/releases"},
   137  	{"/repos/:owner/:repo/releases/:id", "/repos/:owner/:repo/releases/:id"},
   138  	{"/repos/:owner/:repo/releases/:id/assets", "/repos/:owner/:repo/releases/:id/assets"},
   139  	{"/repos/:owner/:repo/stats/contributors", "/repos/:owner/:repo/stats/contributors"},
   140  	{"/repos/:owner/:repo/stats/commit_activity", "/repos/:owner/:repo/stats/commit_activity"},
   141  	{"/repos/:owner/:repo/stats/code_frequency", "/repos/:owner/:repo/stats/code_frequency"},
   142  	{"/repos/:owner/:repo/stats/participation", "/repos/:owner/:repo/stats/participation"},
   143  	{"/repos/:owner/:repo/stats/punch_card", "/repos/:owner/:repo/stats/punch_card"},
   144  	{"/repos/:owner/:repo/statuses/:ref", "/repos/:owner/:repo/statuses/:ref"},
   145  	{"/search/repositories", "/search/repositories"},
   146  	{"/search/code", "/search/code"},
   147  	{"/search/issues", "/search/issues"},
   148  	{"/search/users", "/search/users"},
   149  	{"/legacy/issues/search/:owner/:repository/:state/:keyword", "/legacy/issues/search/:owner/:repository/:state/:keyword"},
   150  	{"/legacy/repos/search/:keyword", "/legacy/repos/search/:keyword"},
   151  	{"/legacy/user/search/:keyword", "/legacy/user/search/:keyword"},
   152  	{"/legacy/user/email/:email", "/legacy/user/email/:email"},
   153  	{"/users/:user", "/users/:user"},
   154  	{"/user", "/user"},
   155  	{"/users", "/users"},
   156  	{"/user/emails", "/user/emails"},
   157  	{"/users/:user/followers", "/users/:user/followers"},
   158  	{"/user/followers", "/user/followers"},
   159  	{"/users/:user/following", "/users/:user/following"},
   160  	{"/user/following", "/user/following"},
   161  	{"/user/following/:user", "/user/following/:user"},
   162  	{"/users/:user/following/:target_user", "/users/:user/following/:target_user"},
   163  	{"/users/:user/keys", "/users/:user/keys"},
   164  	{"/user/keys", "/user/keys"},
   165  	{"/user/keys/:id", "/user/keys/:id"},
   166  	{"/people/:userId", "/people/:userId"},
   167  	{"/people", "/people"},
   168  	{"/activities/:activityId/people/:collection", "/activities/:activityId/people/:collection"},
   169  	{"/people/:userId/people/:collection", "/people/:userId/people/:collection"},
   170  	{"/people/:userId/openIdConnect", "/people/:userId/openIdConnect"},
   171  	{"/people/:userId/activities/:collection", "/people/:userId/activities/:collection"},
   172  	{"/activities/:activityId", "/activities/:activityId"},
   173  	{"/activities", "/activities"},
   174  	{"/activities/:activityId/comments", "/activities/:activityId/comments"},
   175  	{"/comments/:commentId", "/comments/:commentId"},
   176  	{"/people/:userId/moments/:collection", "/people/:userId/moments/:collection"},
   177  }
   178  
   179  type testcase struct {
   180  	path   string
   181  	value  interface{}
   182  	params []denco.Param
   183  	found  bool
   184  }
   185  
   186  func runLookupTest(t *testing.T, records []denco.Record, testcases []testcase) {
   187  	r := denco.New()
   188  	if err := r.Build(records); err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	for _, testcase := range testcases {
   192  		data, params, found := r.Lookup(testcase.path)
   193  		if !reflect.DeepEqual(data, testcase.value) || !reflect.DeepEqual(params, denco.Params(testcase.params)) || !reflect.DeepEqual(found, testcase.found) {
   194  			t.Errorf("Router.Lookup(%q) => (%#v, %#v, %#v), want (%#v, %#v, %#v)", testcase.path, data, params, found, testcase.value, denco.Params(testcase.params), testcase.found)
   195  		}
   196  	}
   197  }
   198  
   199  func TestRouter_Lookup(t *testing.T) {
   200  	testcases := []testcase{
   201  		{"/", "testroute0", nil, true},
   202  		{"/gists/1323/foo/bar", "testroute11", []denco.Param{{"param1", "1323"}}, true},
   203  		{"/gists/1323/foo/133", "testroute12", []denco.Param{{"param1", "1323"}, {"param2", "133"}}, true},
   204  		{"/234/1323/foo/133", "testroute13", []denco.Param{{"param1", "234"}, {"param2", "1323"}, {"param3", "133"}}, true},
   205  		{"/path/to/route", "testroute1", nil, true},
   206  		{"/path/to/other", "testroute2", nil, true},
   207  		{"/path/to/route/a", "testroute3", nil, true},
   208  		{"/path/to/hoge", "testroute4", []denco.Param{{"param", "hoge"}}, true},
   209  		{"/path/to/wildcard/some/params", "testroute5", []denco.Param{{"routepath", "some/params"}}, true},
   210  		{"/path/to/o1/o2", "testroute6", []denco.Param{{"param1", "o1"}, {"param2", "o2"}}, true},
   211  		{"/path/to/p1/sep/p2", "testroute7", []denco.Param{{"param1", "p1"}, {"param2", "p2"}}, true},
   212  		{"/2014/01/06", "testroute8", []denco.Param{{"year", "2014"}, {"month", "01"}, {"day", "06"}}, true},
   213  		{"/user/777", "testroute9", []denco.Param{{"id", "777"}}, true},
   214  		{"/a/to/b/p1/some/wildcard/params", "testroute10", []denco.Param{{"param", "p1"}, {"routepath", "some/wildcard/params"}}, true},
   215  		{"/missing", nil, nil, false},
   216  		{"/path/with/key=value", "testroute14", []denco.Param{{"value", "value"}}, true},
   217  	}
   218  	runLookupTest(t, routes(), testcases)
   219  
   220  	records := []denco.Record{
   221  		{"/", "testroute0"},
   222  		{"/:b", "testroute1"},
   223  		{"/*wildcard", "testroute2"},
   224  	}
   225  	testcases = []testcase{
   226  		{"/", "testroute0", nil, true},
   227  		{"/true", "testroute1", []denco.Param{{"b", "true"}}, true},
   228  		{"/foo/bar", "testroute2", []denco.Param{{"wildcard", "foo/bar"}}, true},
   229  	}
   230  	runLookupTest(t, records, testcases)
   231  
   232  	records = []denco.Record{
   233  		{"/networks/:owner/:repo/events", "testroute0"},
   234  		{"/orgs/:org/events", "testroute1"},
   235  		{"/notifications/threads/:id", "testroute2"},
   236  		{"/mypathisgreat/:thing-id", "testroute3"},
   237  	}
   238  	testcases = []testcase{
   239  		{"/networks/:owner/:repo/events", "testroute0", []denco.Param{{"owner", ":owner"}, {"repo", ":repo"}}, true},
   240  		{"/orgs/:org/events", "testroute1", []denco.Param{{"org", ":org"}}, true},
   241  		{"/notifications/threads/:id", "testroute2", []denco.Param{{"id", ":id"}}, true},
   242  		{"/mypathisgreat/:thing-id", "testroute3", []denco.Param{{"thing-id", ":thing-id"}}, true},
   243  	}
   244  	runLookupTest(t, records, testcases)
   245  
   246  	runLookupTest(t, []denco.Record{
   247  		{"/", "route2"},
   248  	}, []testcase{
   249  		{"/user/alice", nil, nil, false},
   250  	})
   251  
   252  	runLookupTest(t, []denco.Record{
   253  		{"/user/:name", "route1"},
   254  	}, []testcase{
   255  		{"/", nil, nil, false},
   256  	})
   257  
   258  	runLookupTest(t, []denco.Record{
   259  		{"/*wildcard", "testroute0"},
   260  		{"/a/:b", "testroute1"},
   261  	}, []testcase{
   262  		{"/a", "testroute0", []denco.Param{{"wildcard", "a"}}, true},
   263  	})
   264  }
   265  
   266  func TestRouter_Lookup_withManyRoutes(t *testing.T) {
   267  	n := 1000
   268  	records := make([]denco.Record, n)
   269  	for i := 0; i < n; i++ {
   270  		records[i] = denco.Record{Key: "/" + randomString(rand.Intn(50)+10), Value: fmt.Sprintf("route%d", i)} //#nosec
   271  	}
   272  	router := denco.New()
   273  	require.NoError(t, router.Build(records))
   274  
   275  	for _, r := range records {
   276  		data, params, found := router.Lookup(r.Key)
   277  		assert.Equal(t, r.Value, data)
   278  		assert.Empty(t, params)
   279  		assert.True(t, found)
   280  	}
   281  }
   282  
   283  func TestRouter_Lookup_realURIs(t *testing.T) {
   284  	testcases := []testcase{
   285  		{"/authorizations", "/authorizations", nil, true},
   286  		{"/authorizations/1", "/authorizations/:id", []denco.Param{{"id", "1"}}, true},
   287  		{"/applications/1/tokens/zohRoo7e", "/applications/:client_id/tokens/:access_token", []denco.Param{{"client_id", "1"}, {"access_token", "zohRoo7e"}}, true},
   288  		{"/events", "/events", nil, true},
   289  		{"/repos/naoina/denco/events", "/repos/:owner/:repo/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   290  		{"/networks/naoina/denco/events", "/networks/:owner/:repo/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   291  		{"/orgs/something/events", "/orgs/:org/events", []denco.Param{{"org", "something"}}, true},
   292  		{"/users/naoina/received_events", "/users/:user/received_events", []denco.Param{{"user", "naoina"}}, true},
   293  		{"/users/naoina/received_events/public", "/users/:user/received_events/public", []denco.Param{{"user", "naoina"}}, true},
   294  		{"/users/naoina/events", "/users/:user/events", []denco.Param{{"user", "naoina"}}, true},
   295  		{"/users/naoina/events/public", "/users/:user/events/public", []denco.Param{{"user", "naoina"}}, true},
   296  		{"/users/naoina/events/orgs/something", "/users/:user/events/orgs/:org", []denco.Param{{"user", "naoina"}, {"org", "something"}}, true},
   297  		{"/feeds", "/feeds", nil, true},
   298  		{"/notifications", "/notifications", nil, true},
   299  		{"/repos/naoina/denco/notifications", "/repos/:owner/:repo/notifications", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   300  		{"/notifications/threads/1", "/notifications/threads/:id", []denco.Param{{"id", "1"}}, true},
   301  		{"/notifications/threads/2/subscription", "/notifications/threads/:id/subscription", []denco.Param{{"id", "2"}}, true},
   302  		{"/repos/naoina/denco/stargazers", "/repos/:owner/:repo/stargazers", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   303  		{"/users/naoina/starred", "/users/:user/starred", []denco.Param{{"user", "naoina"}}, true},
   304  		{"/user/starred", "/user/starred", nil, true},
   305  		{"/user/starred/naoina/denco", "/user/starred/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   306  		{"/repos/naoina/denco/subscribers", "/repos/:owner/:repo/subscribers", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   307  		{"/users/naoina/subscriptions", "/users/:user/subscriptions", []denco.Param{{"user", "naoina"}}, true},
   308  		{"/user/subscriptions", "/user/subscriptions", nil, true},
   309  		{"/repos/naoina/denco/subscription", "/repos/:owner/:repo/subscription", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   310  		{"/user/subscriptions/naoina/denco", "/user/subscriptions/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   311  		{"/users/naoina/gists", "/users/:user/gists", []denco.Param{{"user", "naoina"}}, true},
   312  		{"/gists", "/gists", nil, true},
   313  		{"/gists/1", "/gists/:id", []denco.Param{{"id", "1"}}, true},
   314  		{"/gists/2/star", "/gists/:id/star", []denco.Param{{"id", "2"}}, true},
   315  		{"/repos/naoina/denco/git/blobs/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/blobs/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
   316  		{"/repos/naoina/denco/git/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/commits/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
   317  		{"/repos/naoina/denco/git/refs", "/repos/:owner/:repo/git/refs", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   318  		{"/repos/naoina/denco/git/tags/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/tags/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
   319  		{"/repos/naoina/denco/git/trees/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/trees/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
   320  		{"/issues", "/issues", nil, true},
   321  		{"/user/issues", "/user/issues", nil, true},
   322  		{"/orgs/something/issues", "/orgs/:org/issues", []denco.Param{{"org", "something"}}, true},
   323  		{"/repos/naoina/denco/issues", "/repos/:owner/:repo/issues", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   324  		{"/repos/naoina/denco/issues/1", "/repos/:owner/:repo/issues/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   325  		{"/repos/naoina/denco/assignees", "/repos/:owner/:repo/assignees", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   326  		{"/repos/naoina/denco/assignees/foo", "/repos/:owner/:repo/assignees/:assignee", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"assignee", "foo"}}, true},
   327  		{"/repos/naoina/denco/issues/1/comments", "/repos/:owner/:repo/issues/:number/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   328  		{"/repos/naoina/denco/issues/1/events", "/repos/:owner/:repo/issues/:number/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   329  		{"/repos/naoina/denco/labels", "/repos/:owner/:repo/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   330  		{"/repos/naoina/denco/labels/bug", "/repos/:owner/:repo/labels/:name", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"name", "bug"}}, true},
   331  		{"/repos/naoina/denco/issues/1/labels", "/repos/:owner/:repo/issues/:number/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   332  		{"/repos/naoina/denco/milestones/1/labels", "/repos/:owner/:repo/milestones/:number/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   333  		{"/repos/naoina/denco/milestones", "/repos/:owner/:repo/milestones", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   334  		{"/repos/naoina/denco/milestones/1", "/repos/:owner/:repo/milestones/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   335  		{"/emojis", "/emojis", nil, true},
   336  		{"/gitignore/templates", "/gitignore/templates", nil, true},
   337  		{"/gitignore/templates/Go", "/gitignore/templates/:name", []denco.Param{{"name", "Go"}}, true},
   338  		{"/meta", "/meta", nil, true},
   339  		{"/rate_limit", "/rate_limit", nil, true},
   340  		{"/users/naoina/orgs", "/users/:user/orgs", []denco.Param{{"user", "naoina"}}, true},
   341  		{"/user/orgs", "/user/orgs", nil, true},
   342  		{"/orgs/something", "/orgs/:org", []denco.Param{{"org", "something"}}, true},
   343  		{"/orgs/something/members", "/orgs/:org/members", []denco.Param{{"org", "something"}}, true},
   344  		{"/orgs/something/members/naoina", "/orgs/:org/members/:user", []denco.Param{{"org", "something"}, {"user", "naoina"}}, true},
   345  		{"/orgs/something/public_members", "/orgs/:org/public_members", []denco.Param{{"org", "something"}}, true},
   346  		{"/orgs/something/public_members/naoina", "/orgs/:org/public_members/:user", []denco.Param{{"org", "something"}, {"user", "naoina"}}, true},
   347  		{"/orgs/something/teams", "/orgs/:org/teams", []denco.Param{{"org", "something"}}, true},
   348  		{"/teams/1", "/teams/:id", []denco.Param{{"id", "1"}}, true},
   349  		{"/teams/2/members", "/teams/:id/members", []denco.Param{{"id", "2"}}, true},
   350  		{"/teams/3/members/naoina", "/teams/:id/members/:user", []denco.Param{{"id", "3"}, {"user", "naoina"}}, true},
   351  		{"/teams/4/repos", "/teams/:id/repos", []denco.Param{{"id", "4"}}, true},
   352  		{"/teams/5/repos/naoina/denco", "/teams/:id/repos/:owner/:repo", []denco.Param{{"id", "5"}, {"owner", "naoina"}, {"repo", "denco"}}, true},
   353  		{"/user/teams", "/user/teams", nil, true},
   354  		{"/repos/naoina/denco/pulls", "/repos/:owner/:repo/pulls", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   355  		{"/repos/naoina/denco/pulls/1", "/repos/:owner/:repo/pulls/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   356  		{"/repos/naoina/denco/pulls/1/commits", "/repos/:owner/:repo/pulls/:number/commits", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   357  		{"/repos/naoina/denco/pulls/1/files", "/repos/:owner/:repo/pulls/:number/files", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   358  		{"/repos/naoina/denco/pulls/1/merge", "/repos/:owner/:repo/pulls/:number/merge", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   359  		{"/repos/naoina/denco/pulls/1/comments", "/repos/:owner/:repo/pulls/:number/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
   360  		{"/user/repos", "/user/repos", nil, true},
   361  		{"/users/naoina/repos", "/users/:user/repos", []denco.Param{{"user", "naoina"}}, true},
   362  		{"/orgs/something/repos", "/orgs/:org/repos", []denco.Param{{"org", "something"}}, true},
   363  		{"/repositories", "/repositories", nil, true},
   364  		{"/repos/naoina/denco", "/repos/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   365  		{"/repos/naoina/denco/contributors", "/repos/:owner/:repo/contributors", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   366  		{"/repos/naoina/denco/languages", "/repos/:owner/:repo/languages", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   367  		{"/repos/naoina/denco/teams", "/repos/:owner/:repo/teams", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   368  		{"/repos/naoina/denco/tags", "/repos/:owner/:repo/tags", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   369  		{"/repos/naoina/denco/branches", "/repos/:owner/:repo/branches", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   370  		{"/repos/naoina/denco/branches/master", "/repos/:owner/:repo/branches/:branch", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"branch", "master"}}, true},
   371  		{"/repos/naoina/denco/collaborators", "/repos/:owner/:repo/collaborators", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   372  		{"/repos/naoina/denco/collaborators/something", "/repos/:owner/:repo/collaborators/:user", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"user", "something"}}, true},
   373  		{"/repos/naoina/denco/comments", "/repos/:owner/:repo/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   374  		{"/repos/naoina/denco/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9/comments", "/repos/:owner/:repo/commits/:sha/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
   375  		{"/repos/naoina/denco/comments/1", "/repos/:owner/:repo/comments/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
   376  		{"/repos/naoina/denco/commits", "/repos/:owner/:repo/commits", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   377  		{"/repos/naoina/denco/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/commits/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
   378  		{"/repos/naoina/denco/readme", "/repos/:owner/:repo/readme", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   379  		{"/repos/naoina/denco/keys", "/repos/:owner/:repo/keys", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   380  		{"/repos/naoina/denco/keys/1", "/repos/:owner/:repo/keys/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
   381  		{"/repos/naoina/denco/downloads", "/repos/:owner/:repo/downloads", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   382  		{"/repos/naoina/denco/downloads/2", "/repos/:owner/:repo/downloads/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "2"}}, true},
   383  		{"/repos/naoina/denco/forks", "/repos/:owner/:repo/forks", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   384  		{"/repos/naoina/denco/hooks", "/repos/:owner/:repo/hooks", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   385  		{"/repos/naoina/denco/hooks/2", "/repos/:owner/:repo/hooks/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "2"}}, true},
   386  		{"/repos/naoina/denco/releases", "/repos/:owner/:repo/releases", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   387  		{"/repos/naoina/denco/releases/1", "/repos/:owner/:repo/releases/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
   388  		{"/repos/naoina/denco/releases/1/assets", "/repos/:owner/:repo/releases/:id/assets", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
   389  		{"/repos/naoina/denco/stats/contributors", "/repos/:owner/:repo/stats/contributors", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   390  		{"/repos/naoina/denco/stats/commit_activity", "/repos/:owner/:repo/stats/commit_activity", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   391  		{"/repos/naoina/denco/stats/code_frequency", "/repos/:owner/:repo/stats/code_frequency", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   392  		{"/repos/naoina/denco/stats/participation", "/repos/:owner/:repo/stats/participation", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   393  		{"/repos/naoina/denco/stats/punch_card", "/repos/:owner/:repo/stats/punch_card", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
   394  		{"/repos/naoina/denco/statuses/master", "/repos/:owner/:repo/statuses/:ref", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"ref", "master"}}, true},
   395  		{"/search/repositories", "/search/repositories", nil, true},
   396  		{"/search/code", "/search/code", nil, true},
   397  		{"/search/issues", "/search/issues", nil, true},
   398  		{"/search/users", "/search/users", nil, true},
   399  		{"/legacy/issues/search/naoina/denco/closed/test", "/legacy/issues/search/:owner/:repository/:state/:keyword", []denco.Param{{"owner", "naoina"}, {"repository", "denco"}, {"state", "closed"}, {"keyword", "test"}}, true},
   400  		{"/legacy/repos/search/test", "/legacy/repos/search/:keyword", []denco.Param{{"keyword", "test"}}, true},
   401  		{"/legacy/user/search/test", "/legacy/user/search/:keyword", []denco.Param{{"keyword", "test"}}, true},
   402  		{"/legacy/user/email/naoina@kuune.org", "/legacy/user/email/:email", []denco.Param{{"email", "naoina@kuune.org"}}, true},
   403  		{"/users/naoina", "/users/:user", []denco.Param{{"user", "naoina"}}, true},
   404  		{"/user", "/user", nil, true},
   405  		{"/users", "/users", nil, true},
   406  		{"/user/emails", "/user/emails", nil, true},
   407  		{"/users/naoina/followers", "/users/:user/followers", []denco.Param{{"user", "naoina"}}, true},
   408  		{"/user/followers", "/user/followers", nil, true},
   409  		{"/users/naoina/following", "/users/:user/following", []denco.Param{{"user", "naoina"}}, true},
   410  		{"/user/following", "/user/following", nil, true},
   411  		{"/user/following/naoina", "/user/following/:user", []denco.Param{{"user", "naoina"}}, true},
   412  		{"/users/naoina/following/target", "/users/:user/following/:target_user", []denco.Param{{"user", "naoina"}, {"target_user", "target"}}, true},
   413  		{"/users/naoina/keys", "/users/:user/keys", []denco.Param{{"user", "naoina"}}, true},
   414  		{"/user/keys", "/user/keys", nil, true},
   415  		{"/user/keys/1", "/user/keys/:id", []denco.Param{{"id", "1"}}, true},
   416  		{"/people/me", "/people/:userId", []denco.Param{{"userId", "me"}}, true},
   417  		{"/people", "/people", nil, true},
   418  		{"/activities/foo/people/vault", "/activities/:activityId/people/:collection", []denco.Param{{"activityId", "foo"}, {"collection", "vault"}}, true},
   419  		{"/people/me/people/vault", "/people/:userId/people/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true},
   420  		{"/people/me/openIdConnect", "/people/:userId/openIdConnect", []denco.Param{{"userId", "me"}}, true},
   421  		{"/people/me/activities/vault", "/people/:userId/activities/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true},
   422  		{"/activities/foo", "/activities/:activityId", []denco.Param{{"activityId", "foo"}}, true},
   423  		{"/activities", "/activities", nil, true},
   424  		{"/activities/foo/comments", "/activities/:activityId/comments", []denco.Param{{"activityId", "foo"}}, true},
   425  		{"/comments/hoge", "/comments/:commentId", []denco.Param{{"commentId", "hoge"}}, true},
   426  		{"/people/me/moments/vault", "/people/:userId/moments/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true},
   427  	}
   428  	runLookupTest(t, realURIs, testcases)
   429  }
   430  
   431  func TestRouter_Build(t *testing.T) {
   432  	// test for duplicate name of path parameters.
   433  	func() {
   434  		r := denco.New()
   435  		require.Errorf(t,
   436  			r.Build([]denco.Record{
   437  				{"/:user/:id/:id", "testroute0"},
   438  				{"/:user/:user/:id", "testroute0"},
   439  			}),
   440  			"no error returned by duplicate name of path parameters",
   441  		)
   442  	}()
   443  }
   444  
   445  func TestRouter_Build_withoutSizeHint(t *testing.T) {
   446  	for _, v := range []struct {
   447  		keys     []string
   448  		sizeHint int
   449  	}{
   450  		{[]string{"/user"}, 0},
   451  		{[]string{"/user/:id"}, 1},
   452  		{[]string{"/user/:id/post"}, 1},
   453  		{[]string{"/user/:id/post:validate"}, 2},
   454  		{[]string{"/user/:id/:group"}, 2},
   455  		{[]string{"/user/:id/post/:cid"}, 2},
   456  		{[]string{"/user/:id/post/:cid", "/admin/:id/post/:cid"}, 2},
   457  		{[]string{"/user/:id", "/admin/:id/post/:cid"}, 2},
   458  		{[]string{"/user/:id/post/:cid", "/admin/:id/post/:cid/:type"}, 3},
   459  	} {
   460  		r := denco.New()
   461  		actual := r.SizeHint
   462  		expected := -1
   463  
   464  		assert.Equalf(t, expected, actual, `before Build; Router.SizeHint => (%[1]T=%#[1]v); want (%[2]T=%#[2]v)`, actual, expected)
   465  		records := make([]denco.Record, len(v.keys))
   466  		for i, k := range v.keys {
   467  			records[i] = denco.Record{Key: k, Value: "value"}
   468  		}
   469  		require.NoError(t, r.Build(records))
   470  		actual = r.SizeHint
   471  		expected = v.sizeHint
   472  		assert.Equalf(t, expected, actual, `Router.Build(%#v); Router.SizeHint => (%[2]T=%#[2]v); want (%[3]T=%#[3]v)`, records, actual, expected)
   473  	}
   474  }
   475  
   476  func TestRouter_Build_withSizeHint(t *testing.T) {
   477  	for _, v := range []struct {
   478  		key      string
   479  		sizeHint int
   480  		expect   int
   481  	}{
   482  		{"/user", 0, 0},
   483  		{"/user", 1, 1},
   484  		{"/user", 2, 2},
   485  		{"/user/:id", 3, 3},
   486  		{"/user/:id/:group", 0, 0},
   487  		{"/user/:id/:group", 1, 1},
   488  		{"/user/:id/:group:validate", 1, 1},
   489  	} {
   490  		r := denco.New()
   491  		r.SizeHint = v.sizeHint
   492  		records := []denco.Record{
   493  			{v.key, "value"},
   494  		}
   495  		require.NoError(t, r.Build(records))
   496  		actual := r.SizeHint
   497  		expected := v.expect
   498  		assert.Equalf(t, expected, actual, `Router.Build(%#v); Router.SizeHint => (%[2]T=%#[2]v); want (%[3]T=%#[3]v)`, records, actual, expected)
   499  	}
   500  }
   501  
   502  func TestParams_Get(t *testing.T) {
   503  	params := denco.Params([]denco.Param{
   504  		{"name1", "value1"},
   505  		{"name2", "value2"},
   506  		{"name3", "value3"},
   507  		{"name1", "value4"},
   508  	})
   509  	for _, v := range []struct{ value, expected string }{
   510  		{"name1", "value1"},
   511  		{"name2", "value2"},
   512  		{"name3", "value3"},
   513  		{"name4", ""},
   514  	} {
   515  		actual := params.Get(v.value)
   516  		expected := v.expected
   517  		assert.Equal(t, expected, actual, "Params.Get(%q) => %#v, want %#v", v.value, actual, expected)
   518  	}
   519  }
   520  

View as plain text