package gig
import (
"strings"
"testing"
"github.com/matryer/is"
)
var (
staticRoutes = []*Route{
{"/", ""},
{"/cmd.html", ""},
{"/code.html", ""},
{"/contrib.html", ""},
{"/contribute.html", ""},
{"/debugging_with_gdb.html", ""},
{"/docs.html", ""},
{"/effective_go.html", ""},
{"/files.log", ""},
{"/gccgo_contribute.html", ""},
{"/gccgo_install.html", ""},
{"/go-logo-black.png", ""},
{"/go-logo-blue.png", ""},
{"/go-logo-white.png", ""},
{"/go1.1.html", ""},
{"/go1.2.html", ""},
{"/go1.html", ""},
{"/go1compat.html", ""},
{"/go_faq.html", ""},
{"/go_mem.html", ""},
{"/go_spec.html", ""},
{"/help.html", ""},
{"/ie.css", ""},
{"/install-source.html", ""},
{"/install.html", ""},
{"/logo-153x55.png", ""},
{"/Makefile", ""},
{"/root.html", ""},
{"/share.png", ""},
{"/sieve.gif", ""},
{"/tos.html", ""},
{"/articles/", ""},
{"/articles/go_command.html", ""},
{"/articles/index.html", ""},
{"/articles/wiki/", ""},
{"/articles/wiki/edit.html", ""},
{"/articles/wiki/final-noclosure.go", ""},
{"/articles/wiki/final-noerror.go", ""},
{"/articles/wiki/final-parsetemplate.go", ""},
{"/articles/wiki/final-template.go", ""},
{"/articles/wiki/final.go", ""},
{"/articles/wiki/get.go", ""},
{"/articles/wiki/http-sample.go", ""},
{"/articles/wiki/index.html", ""},
{"/articles/wiki/Makefile", ""},
{"/articles/wiki/notemplate.go", ""},
{"/articles/wiki/part1-noerror.go", ""},
{"/articles/wiki/part1.go", ""},
{"/articles/wiki/part2.go", ""},
{"/articles/wiki/part3-errorhandling.go", ""},
{"/articles/wiki/part3.go", ""},
{"/articles/wiki/test.bash", ""},
{"/articles/wiki/test_edit.good", ""},
{"/articles/wiki/test_Test.txt.good", ""},
{"/articles/wiki/test_view.good", ""},
{"/articles/wiki/view.html", ""},
{"/codewalk/", ""},
{"/codewalk/codewalk.css", ""},
{"/codewalk/codewalk.js", ""},
{"/codewalk/codewalk.xml", ""},
{"/codewalk/functions.xml", ""},
{"/codewalk/markov.go", ""},
{"/codewalk/markov.xml", ""},
{"/codewalk/pig.go", ""},
{"/codewalk/popout.png", ""},
{"/codewalk/run", ""},
{"/codewalk/sharemem.xml", ""},
{"/codewalk/urlpoll.go", ""},
{"/devel/", ""},
{"/devel/release.html", ""},
{"/devel/weekly.html", ""},
{"/gopher/", ""},
{"/gopher/appenginegopher.jpg", ""},
{"/gopher/appenginegophercolor.jpg", ""},
{"/gopher/appenginelogo.gif", ""},
{"/gopher/bumper.png", ""},
{"/gopher/bumper192x108.png", ""},
{"/gopher/bumper320x180.png", ""},
{"/gopher/bumper480x270.png", ""},
{"/gopher/bumper640x360.png", ""},
{"/gopher/doc.png", ""},
{"/gopher/frontpage.png", ""},
{"/gopher/gopherbw.png", ""},
{"/gopher/gophercolor.png", ""},
{"/gopher/gophercolor16x16.png", ""},
{"/gopher/help.png", ""},
{"/gopher/pkg.png", ""},
{"/gopher/project.png", ""},
{"/gopher/ref.png", ""},
{"/gopher/run.png", ""},
{"/gopher/talks.png", ""},
{"/gopher/pencil/", ""},
{"/gopher/pencil/gopherhat.jpg", ""},
{"/gopher/pencil/gopherhelmet.jpg", ""},
{"/gopher/pencil/gophermega.jpg", ""},
{"/gopher/pencil/gopherrunning.jpg", ""},
{"/gopher/pencil/gopherswim.jpg", ""},
{"/gopher/pencil/gopherswrench.jpg", ""},
{"/play/", ""},
{"/play/fib.go", ""},
{"/play/hello.go", ""},
{"/play/life.go", ""},
{"/play/peano.go", ""},
{"/play/pi.go", ""},
{"/play/sieve.go", ""},
{"/play/solitaire.go", ""},
{"/play/tree.go", ""},
{"/progs/", ""},
{"/progs/cgo1.go", ""},
{"/progs/cgo2.go", ""},
{"/progs/cgo3.go", ""},
{"/progs/cgo4.go", ""},
{"/progs/defer.go", ""},
{"/progs/defer.out", ""},
{"/progs/defer2.go", ""},
{"/progs/defer2.out", ""},
{"/progs/eff_bytesize.go", ""},
{"/progs/eff_bytesize.out", ""},
{"/progs/eff_qr.go", ""},
{"/progs/eff_sequence.go", ""},
{"/progs/eff_sequence.out", ""},
{"/progs/eff_unused1.go", ""},
{"/progs/eff_unused2.go", ""},
{"/progs/error.go", ""},
{"/progs/error2.go", ""},
{"/progs/error3.go", ""},
{"/progs/error4.go", ""},
{"/progs/go1.go", ""},
{"/progs/gobs1.go", ""},
{"/progs/gobs2.go", ""},
{"/progs/image_draw.go", ""},
{"/progs/image_package1.go", ""},
{"/progs/image_package1.out", ""},
{"/progs/image_package2.go", ""},
{"/progs/image_package2.out", ""},
{"/progs/image_package3.go", ""},
{"/progs/image_package3.out", ""},
{"/progs/image_package4.go", ""},
{"/progs/image_package4.out", ""},
{"/progs/image_package5.go", ""},
{"/progs/image_package5.out", ""},
{"/progs/image_package6.go", ""},
{"/progs/image_package6.out", ""},
{"/progs/interface.go", ""},
{"/progs/interface2.go", ""},
{"/progs/interface2.out", ""},
{"/progs/json1.go", ""},
{"/progs/json2.go", ""},
{"/progs/json2.out", ""},
{"/progs/json3.go", ""},
{"/progs/json4.go", ""},
{"/progs/json5.go", ""},
{"/progs/run", ""},
{"/progs/slices.go", ""},
{"/progs/timeout1.go", ""},
{"/progs/timeout2.go", ""},
{"/progs/update.bash", ""},
}
gitHubAPI = []*Route{
{"/applications/:client_id/tokens", ""},
{"/applications/:client_id/tokens/:access_token", ""},
{"/authorizations", ""},
{"/authorizations/:id", ""},
{"/authorizations/clients/:client_id", ""},
{"/emojis", ""},
{"/events", ""},
{"/feeds", ""},
{"/gists", ""},
{"/gists/:id", ""},
{"/gists/:id/forks", ""},
{"/gists/:id/star", ""},
{"/gists/public", ""},
{"/gists/starred", ""},
{"/gitignore/templates", ""},
{"/gitignore/templates/:name", ""},
{"/issues", ""},
{"/legacy/issues/search/:owner/:repository/:state/:keyword", ""},
{"/legacy/repos/search/:keyword", ""},
{"/legacy/user/email/:email", ""},
{"/legacy/user/search/:keyword", ""},
{"/markdown", ""},
{"/markdown/raw", ""},
{"/meta", ""},
{"/networks/:owner/:repo/events", ""},
{"/notifications", ""},
{"/notifications/threads/:id", ""},
{"/notifications/threads/:id/subscription", ""},
{"/orgs/:org", ""},
{"/orgs/:org/events", ""},
{"/orgs/:org/issues", ""},
{"/orgs/:org/members", ""},
{"/orgs/:org/members/:user", ""},
{"/orgs/:org/public_members", ""},
{"/orgs/:org/public_members/:user", ""},
{"/orgs/:org/repos", ""},
{"/orgs/:org/teams", ""},
{"/rate_limit", ""},
{"/repos/:owner/:repo", ""},
{"/repos/:owner/:repo/:archive_format/:ref", ""},
{"/repos/:owner/:repo/assignees", ""},
{"/repos/:owner/:repo/assignees/:assignee", ""},
{"/repos/:owner/:repo/branches", ""},
{"/repos/:owner/:repo/branches/:branch", ""},
{"/repos/:owner/:repo/collaborators", ""},
{"/repos/:owner/:repo/collaborators/:user", ""},
{"/repos/:owner/:repo/comments", ""},
{"/repos/:owner/:repo/comments/:id", ""},
{"/repos/:owner/:repo/commits", ""},
{"/repos/:owner/:repo/commits/:sha", ""},
{"/repos/:owner/:repo/commits/:sha/comments", ""},
{"/repos/:owner/:repo/contents/*path", ""},
{"/repos/:owner/:repo/contributors", ""},
{"/repos/:owner/:repo/downloads", ""},
{"/repos/:owner/:repo/downloads/:id", ""},
{"/repos/:owner/:repo/events", ""},
{"/repos/:owner/:repo/forks", ""},
{"/repos/:owner/:repo/git/blobs", ""},
{"/repos/:owner/:repo/git/blobs/:sha", ""},
{"/repos/:owner/:repo/git/commits", ""},
{"/repos/:owner/:repo/git/commits/:sha", ""},
{"/repos/:owner/:repo/git/refs", ""},
{"/repos/:owner/:repo/git/refs/*ref", ""},
{"/repos/:owner/:repo/git/tags", ""},
{"/repos/:owner/:repo/git/tags/:sha", ""},
{"/repos/:owner/:repo/git/trees", ""},
{"/repos/:owner/:repo/git/trees/:sha", ""},
{"/repos/:owner/:repo/hooks", ""},
{"/repos/:owner/:repo/hooks/:id", ""},
{"/repos/:owner/:repo/hooks/:id/tests", ""},
{"/repos/:owner/:repo/issues", ""},
{"/repos/:owner/:repo/issues/:number", ""},
{"/repos/:owner/:repo/issues/:number/comments", ""},
{"/repos/:owner/:repo/issues/:number/events", ""},
{"/repos/:owner/:repo/issues/:number/labels", ""},
{"/repos/:owner/:repo/issues/:number/labels/:name", ""},
{"/repos/:owner/:repo/issues/comments", ""},
{"/repos/:owner/:repo/issues/comments/:id", ""},
{"/repos/:owner/:repo/issues/events", ""},
{"/repos/:owner/:repo/issues/events/:id", ""},
{"/repos/:owner/:repo/keys", ""},
{"/repos/:owner/:repo/keys/:id", ""},
{"/repos/:owner/:repo/labels", ""},
{"/repos/:owner/:repo/labels/:name", ""},
{"/repos/:owner/:repo/languages", ""},
{"/repos/:owner/:repo/merges", ""},
{"/repos/:owner/:repo/milestones", ""},
{"/repos/:owner/:repo/milestones/:number", ""},
{"/repos/:owner/:repo/milestones/:number/labels", ""},
{"/repos/:owner/:repo/notifications", ""},
{"/repos/:owner/:repo/pulls", ""},
{"/repos/:owner/:repo/pulls/:number", ""},
{"/repos/:owner/:repo/pulls/:number/comments", ""},
{"/repos/:owner/:repo/pulls/:number/commits", ""},
{"/repos/:owner/:repo/pulls/:number/files", ""},
{"/repos/:owner/:repo/pulls/:number/merge", ""},
{"/repos/:owner/:repo/pulls/comments", ""},
{"/repos/:owner/:repo/pulls/comments/:number", ""},
{"/repos/:owner/:repo/readme", ""},
{"/repos/:owner/:repo/releases", ""},
{"/repos/:owner/:repo/releases/:id", ""},
{"/repos/:owner/:repo/releases/:id/assets", ""},
{"/repos/:owner/:repo/stargazers", ""},
{"/repos/:owner/:repo/stats/code_frequency", ""},
{"/repos/:owner/:repo/stats/commit_activity", ""},
{"/repos/:owner/:repo/stats/contributors", ""},
{"/repos/:owner/:repo/stats/participation", ""},
{"/repos/:owner/:repo/stats/punch_card", ""},
{"/repos/:owner/:repo/statuses/:ref", ""},
{"/repos/:owner/:repo/subscribers", ""},
{"/repos/:owner/:repo/subscription", ""},
{"/repos/:owner/:repo/tags", ""},
{"/repos/:owner/:repo/teams", ""},
{"/repositories", ""},
{"/search/code", ""},
{"/search/issues", ""},
{"/search/repositories", ""},
{"/search/users", ""},
{"/teams/:id", ""},
{"/teams/:id/members", ""},
{"/teams/:id/members/:user", ""},
{"/teams/:id/repos", ""},
{"/teams/:id/repos/:owner/:repo", ""},
{"/user", ""},
{"/user/emails", ""},
{"/user/followers", ""},
{"/user/following", ""},
{"/user/following/:user", ""},
{"/user/issues", ""},
{"/user/keys", ""},
{"/user/keys/:id", ""},
{"/user/orgs", ""},
{"/user/repos", ""},
{"/user/starred", ""},
{"/user/starred/:owner/:repo", ""},
{"/user/subscriptions", ""},
{"/user/subscriptions/:owner/:repo", ""},
{"/user/teams", ""},
{"/users", ""},
{"/users/:user", ""},
{"/users/:user/events", ""},
{"/users/:user/events/orgs/:org", ""},
{"/users/:user/events/public", ""},
{"/users/:user/followers", ""},
{"/users/:user/following", ""},
{"/users/:user/following/:target_user", ""},
{"/users/:user/gists", ""},
{"/users/:user/keys", ""},
{"/users/:user/orgs", ""},
{"/users/:user/received_events", ""},
{"/users/:user/received_events/public", ""},
{"/users/:user/repos", ""},
{"/users/:user/starred", ""},
{"/users/:user/subscriptions", ""},
}
parseAPI = []*Route{
{"/1/classes/:className", ""},
{"/1/classes/:className/:objectId", ""},
{"/1/events/:eventName", ""},
{"/1/files/:fileName", ""},
{"/1/functions", ""},
{"/1/installations", ""},
{"/1/installations/:objectId", ""},
{"/1/login", ""},
{"/1/push", ""},
{"/1/requestPasswordReset", ""},
{"/1/roles", ""},
{"/1/roles/:objectId", ""},
{"/1/users", ""},
{"/1/users/:objectId", ""},
}
googlePlusAPI = []*Route{
// People
{"/people/:userId", ""},
{"/people", ""},
{"/activities/:activityId/people/:collection", ""},
{"/people/:userId/people/:collection", ""},
{"/people/:userId/openIdConnect", ""},
// Activities
{"/people/:userId/activities/:collection", ""},
{"/activities/:activityId", ""},
{"/activities", ""},
// Comments
{"/activities/:activityId/comments", ""},
{"/comments/:commentId", ""},
// Moments
{"/people/:userId/moments/:collection", ""},
{"/people/:userId/moments/:collection", ""},
{"/moments/:id", ""},
}
// handlerHelper created a function that will set a context key for assertion.
handlerHelper = func(key string, value int) func(c Context) error {
return func(c Context) error {
c.Set(key, value)
c.Set("path", c.Path())
return nil
}
}
)
func TestRouterEmpty(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
path := ""
r.add(path, func(c Context) error {
c.Set("path", path)
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find(path, c)
is.NoErr(c.handler(c))
is.Equal(path, c.Get("path"))
}
func TestRouterStatic(t *testing.T) {
g := New()
r := g.router
path := "/folders/a/files/gig.gif"
r.add(path, func(c Context) error {
c.Set("path", path)
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find(path, c)
is := is.New(t)
is.NoErr(c.handler(c))
is.Equal(path, c.Get("path"))
}
func TestRouterParam(t *testing.T) {
g := New()
r := g.router
r.add("/users/:id", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/users/1", c)
is := is.New(t)
is.Equal("1", c.Param("id"))
}
func TestRouterTwoParam(t *testing.T) {
g := New()
r := g.router
r.add("/users/:uid/files/:fid", func(Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/users/1/files/1", c)
is := is.New(t)
is.Equal("1", c.Param("uid"))
is.Equal("1", c.Param("fid"))
}
func TestRouterParamWithSlash(t *testing.T) {
g := New()
r := g.router
r.add("/a/:b/c/d/:g", func(c Context) error {
return nil
})
r.add("/a/:b/c/:d/:f", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
// No Panic
r.find("/a/1/c/d/2/3", c)
}
func TestRouterParamStaticConflict(t *testing.T) {
g := New()
r := g.router
handler := func(c Context) error {
c.Set("path", c.Path())
return nil
}
gr := g.Group("/g")
gr.Handle("/skills", handler)
gr.Handle("/status", handler)
gr.Handle("/:name", handler)
is := is.New(t)
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/g/s", c)
is.NoErr(c.handler(c))
is.Equal("s", c.Param("name"))
is.Equal("/g/:name", c.Get("path"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/g/status", c)
is.NoErr(c.handler(c))
is.Equal("/g/status", c.Get("path"))
}
func TestRouterMatchAny(t *testing.T) {
g := New()
r := g.router
// Routes
r.add("/", func(Context) error {
return nil
})
r.add("/*", func(Context) error {
return nil
})
r.add("/users/*", func(Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
is := is.New(t)
r.find("/", c)
is.Equal("", c.Param("*"))
r.find("/download", c)
is.Equal("download", c.Param("*"))
r.find("/users/joe", c)
is.Equal("joe", c.Param("*"))
}
// TestRouterMatchAnySlash shall verify finding the best route
// for any routes with trailing slash requests.
func TestRouterMatchAnySlash(t *testing.T) {
g := New()
r := g.router
is := is.New(t)
handler := func(c Context) error {
c.Set("path", c.Path())
return nil
}
// Routes
r.add("/users", handler)
r.add("/users/*", handler)
r.add("/img/*", handler)
r.add("/img/load", handler)
r.add("/img/load/*", handler)
r.add("/assets/*", handler)
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/", c)
is.Equal("", c.Param("*"))
// Test trailing slash request for simple any route (see #1526)
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/", c)
is.NoErr(c.handler(c))
is.Equal("/users/*", c.Get("path"))
is.Equal("", c.Param("*"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/joe", c)
is.NoErr(c.handler(c))
is.Equal("/users/*", c.Get("path"))
is.Equal("joe", c.Param("*"))
// Test trailing slash request for nested any route (see #1526)
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/img/load", c)
is.NoErr(c.handler(c))
is.Equal("/img/load", c.Get("path"))
is.Equal("", c.Param("*"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/img/load/", c)
is.NoErr(c.handler(c))
is.Equal("/img/load/*", c.Get("path"))
is.Equal("", c.Param("*"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/img/load/ben", c)
is.NoErr(c.handler(c))
is.Equal("/img/load/*", c.Get("path"))
is.Equal("ben", c.Param("*"))
// Test /assets/* any route
// ... without trailing slash must not match
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/assets", c)
is.True(c.handler(c) != nil)
is.Equal(nil, c.Get("path"))
is.Equal("", c.Param("*"))
// ... with trailing slash must match
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/assets/", c)
is.NoErr(c.handler(c))
is.Equal("/assets/*", c.Get("path"))
is.Equal("", c.Param("*"))
}
func TestRouterMatchAnyMultiLevel(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
handler := func(c Context) error {
c.Set("path", c.Path())
return nil
}
// Routes
r.add("/api/users/jack", handler)
r.add("/api/users/jill", handler)
r.add("/api/users/*", handler)
r.add("/api/*", handler)
r.add("/other/*", handler)
r.add("/*", handler)
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/api/users/jack", c)
is.NoErr(c.handler(c))
is.Equal("/api/users/jack", c.Get("path"))
is.Equal("", c.Param("*"))
r.find("/api/users/jill", c)
is.NoErr(c.handler(c))
is.Equal("/api/users/jill", c.Get("path"))
is.Equal("", c.Param("*"))
r.find("/api/users/joe", c)
is.NoErr(c.handler(c))
is.Equal("/api/users/*", c.Get("path"))
is.Equal("joe", c.Param("*"))
r.find("/api/nousers/joe", c)
is.NoErr(c.handler(c))
is.Equal("/api/*", c.Get("path"))
is.Equal("nousers/joe", c.Param("*"))
r.find("/api/none", c)
is.NoErr(c.handler(c))
is.Equal("/api/*", c.Get("path"))
is.Equal("none", c.Param("*"))
r.find("/noapi/users/jim", c)
is.NoErr(c.handler(c))
is.Equal("/*", c.Get("path"))
is.Equal("noapi/users/jim", c.Param("*"))
}
func TestRouterMatchAnyMultiLevelWithPost(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
handler := func(c Context) error {
c.Set("path", c.Path())
return nil
}
// Routes
g.Handle("/api/auth/login", handler)
g.Handle("/api/auth/forgotPassword", handler)
g.Handle("/api/*", handler)
g.Handle("/*", handler)
// /api/auth/login shall choose login
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/api/auth/login", c)
is.NoErr(c.handler(c))
is.Equal("/api/auth/login", c.Get("path"))
is.Equal("", c.Param("*"))
// /api/auth/login shall choose any route
// c = g.newContext(nil, nil,nil).(*context)
// r.find( "/api/auth/login", c)
// c.handler(c)
// is.Equal("/api/*", c.Get("path"))
// is.Equal("auth/login", c.Param("*"))
// /api/auth/logout shall choose nearest any route
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/api/auth/logout", c)
is.NoErr(c.handler(c))
is.Equal("/api/*", c.Get("path"))
is.Equal("auth/logout", c.Param("*"))
// /api/other/test shall choose nearest any route
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/api/other/test", c)
is.NoErr(c.handler(c))
is.Equal("/api/*", c.Get("path"))
is.Equal("other/test", c.Param("*"))
// /api/other/test shall choose nearest any route
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/api/other/test", c)
is.NoErr(c.handler(c))
is.Equal("/api/*", c.Get("path"))
is.Equal("other/test", c.Param("*"))
}
func TestRouterMicroParam(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
r.add("/:a/:b/:c", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/1/2/3", c)
is.Equal("1", c.Param("a"))
is.Equal("2", c.Param("b"))
is.Equal("3", c.Param("c"))
}
func TestRouterMixParamMatchAny(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
// Route
r.add("/users/:id/*", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/users/joe/comments", c)
is.NoErr(c.handler(c))
is.Equal("joe", c.Param("id"))
}
func TestRouterMultiRoute(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
// Routes
r.add("/users", func(c Context) error {
c.Set("path", "/users")
return nil
})
r.add("/users/:id", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
// Route > /users
r.find("/users", c)
is.NoErr(c.handler(c))
is.Equal("/users", c.Get("path"))
// Route > /users/:id
r.find("/users/1", c)
is.Equal("1", c.Param("id"))
// Route > /user
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/user", c)
he := c.handler(c).(*GeminiError)
is.Equal(StatusNotFound, he.Code)
}
func TestRouterPriority(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
// Routes
r.add("/users", handlerHelper("a", 1))
r.add("/users/new", handlerHelper("b", 2))
r.add("/users/:id", handlerHelper("c", 3))
r.add("/users/dew", handlerHelper("d", 4))
r.add("/users/:id/files", handlerHelper("g", 5))
r.add("/users/newsee", handlerHelper("f", 6))
r.add("/users/*", handlerHelper("g", 7))
r.add("/users/new/*", handlerHelper("h", 8))
r.add("/*", handlerHelper("i", 9))
// Route > /users
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/users", c)
is.NoErr(c.handler(c))
is.Equal(1, c.Get("a"))
is.Equal("/users", c.Get("path"))
// Route > /users/new
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/new", c)
is.NoErr(c.handler(c))
is.Equal(2, c.Get("b"))
is.Equal("/users/new", c.Get("path"))
// Route > /users/:id
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/1", c)
is.NoErr(c.handler(c))
is.Equal(3, c.Get("c"))
is.Equal("/users/:id", c.Get("path"))
// Route > /users/dew
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/dew", c)
is.NoErr(c.handler(c))
is.Equal(4, c.Get("d"))
is.Equal("/users/dew", c.Get("path"))
// Route > /users/:id/files
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/1/files", c)
is.NoErr(c.handler(c))
is.Equal(5, c.Get("g"))
is.Equal("/users/:id/files", c.Get("path"))
// Route > /users/:id
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/news", c)
is.NoErr(c.handler(c))
is.Equal(3, c.Get("c"))
is.Equal("/users/:id", c.Get("path"))
// Route > /users/newsee
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/newsee", c)
is.NoErr(c.handler(c))
is.Equal(6, c.Get("f"))
is.Equal("/users/newsee", c.Get("path"))
// Route > /users/newsee
r.find("/users/newsee", c)
is.NoErr(c.handler(c))
is.Equal(6, c.Get("f"))
// Route > /users/newsee
r.find("/users/newsee", c)
is.NoErr(c.handler(c))
is.Equal(6, c.Get("f"))
// Route > /users/*
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/joe/books", c)
is.NoErr(c.handler(c))
is.Equal(7, c.Get("g"))
is.Equal("/users/*", c.Get("path"))
is.Equal("joe/books", c.Param("*"))
// Route > /users/new/* should be matched
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/new/someone", c)
is.NoErr(c.handler(c))
is.Equal(8, c.Get("h"))
is.Equal("/users/new/*", c.Get("path"))
is.Equal("someone", c.Param("*"))
// Route > /users/* should be matched although /users/dew exists
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/dew/someone", c)
is.NoErr(c.handler(c))
is.Equal(7, c.Get("g"))
is.Equal("/users/*", c.Get("path"))
is.Equal("dew/someone", c.Param("*"))
// Route > /users/* should be matched although /users/dew exists
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/notexists/someone", c)
is.NoErr(c.handler(c))
is.Equal(7, c.Get("g"))
is.Equal("/users/*", c.Get("path"))
is.Equal("notexists/someone", c.Param("*"))
// Route > *
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/nousers", c)
is.NoErr(c.handler(c))
is.Equal(9, c.Get("i"))
is.Equal("/*", c.Get("path"))
is.Equal("nousers", c.Param("*"))
// Route > *
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/nousers/new", c)
is.NoErr(c.handler(c))
is.Equal(9, c.Get("i"))
is.Equal("/*", c.Get("path"))
is.Equal("nousers/new", c.Param("*"))
}
func TestRouterIssue1348(t *testing.T) {
g := New()
r := g.router
r.add("/:lang/", func(c Context) error {
return nil
})
r.add("/:lang/dupa", func(c Context) error {
return nil
})
}
func TestRouterPriorityNotFound(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
c := g.newContext(nil, nil, "", nil).(*context)
// Add
r.add("/a/foo", func(c Context) error {
c.Set("a", 1)
return nil
})
r.add("/a/bar", func(c Context) error {
c.Set("b", 2)
return nil
})
// Find
r.find("/a/foo", c)
is.NoErr(c.handler(c))
is.Equal(1, c.Get("a"))
r.find("/a/bar", c)
is.NoErr(c.handler(c))
is.Equal(2, c.Get("b"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/abc/def", c)
he := c.handler(c).(*GeminiError)
is.Equal(StatusNotFound, he.Code)
}
func TestRouterParamNames(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
// Routes
r.add("/users", func(c Context) error {
c.Set("path", "/users")
return nil
})
r.add("/users/:id", func(c Context) error {
return nil
})
r.add("/users/:uid/files/:fid", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
// Route > /users
r.find("/users", c)
is.NoErr(c.handler(c))
is.Equal("/users", c.Get("path"))
// Route > /users/:id
r.find("/users/1", c)
is.Equal("id", c.pnames[0])
is.Equal("1", c.Param("id"))
// Route > /users/:uid/files/:fid
r.find("/users/1/files/1", c)
is.Equal("uid", c.pnames[0])
is.Equal("1", c.Param("uid"))
is.Equal("fid", c.pnames[1])
is.Equal("1", c.Param("fid"))
}
func TestRouterStaticDynamicConflict(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
r.add("/dictionary/skills", handlerHelper("a", 1))
r.add("/dictionary/:name", handlerHelper("b", 2))
r.add("/users/new", handlerHelper("d", 4))
r.add("/users/:name", handlerHelper("g", 5))
r.add("/server", handlerHelper("c", 3))
r.add("/", handlerHelper("f", 6))
c := g.newContext(nil, nil, "", nil)
r.find("/dictionary/skills", c)
is.NoErr(c.Handler()(c))
is.Equal(1, c.Get("a"))
is.Equal("/dictionary/skills", c.Get("path"))
c = g.newContext(nil, nil, "", nil)
r.find("/dictionary/skillsnot", c)
is.NoErr(c.Handler()(c))
is.Equal(2, c.Get("b"))
is.Equal("/dictionary/:name", c.Get("path"))
c = g.newContext(nil, nil, "", nil)
r.find("/dictionary/type", c)
is.NoErr(c.Handler()(c))
is.Equal(2, c.Get("b"))
is.Equal("/dictionary/:name", c.Get("path"))
c = g.newContext(nil, nil, "", nil)
r.find("/server", c)
is.NoErr(c.Handler()(c))
is.Equal(3, c.Get("c"))
is.Equal("/server", c.Get("path"))
c = g.newContext(nil, nil, "", nil)
r.find("/users/new", c)
is.NoErr(c.Handler()(c))
is.Equal(4, c.Get("d"))
is.Equal("/users/new", c.Get("path"))
c = g.newContext(nil, nil, "", nil)
r.find("/users/new2", c)
is.NoErr(c.Handler()(c))
is.Equal(5, c.Get("g"))
is.Equal("/users/:name", c.Get("path"))
c = g.newContext(nil, nil, "", nil)
r.find("/", c)
is.NoErr(c.Handler()(c))
is.Equal(6, c.Get("f"))
is.Equal("/", c.Get("path"))
}
func TestRouterParamBacktraceNotFound(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
// Add
r.add("/:param1", func(c Context) error {
return nil
})
r.add("/:param1/foo", func(c Context) error {
return nil
})
r.add("/:param1/bar", func(c Context) error {
return nil
})
r.add("/:param1/bar/:param2", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
// Find
r.find("/a", c)
is.Equal("a", c.Param("param1"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/a/foo", c)
is.Equal("a", c.Param("param1"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/a/bar", c)
is.Equal("a", c.Param("param1"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/a/bar/b", c)
is.Equal("a", c.Param("param1"))
is.Equal("b", c.Param("param2"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/a/bbbbb", c)
he := c.handler(c).(*GeminiError)
is.Equal(StatusNotFound, he.Code)
}
func testRouterAPI(t *testing.T, api []*Route) {
is := is.New(t)
g := New()
r := g.router
for _, route := range api {
r.add(route.Path, func(c Context) error {
return nil
})
}
c := g.newContext(nil, nil, "", nil).(*context)
for _, route := range api {
r.find(route.Path, c)
tokens := strings.Split(route.Path[1:], "/")
for _, token := range tokens {
if token[0] == ':' {
is.Equal(c.Param(token[1:]), token)
}
}
}
}
func TestRouterGitHubAPI(t *testing.T) {
testRouterAPI(t, gitHubAPI)
}
func TestRouterParamAlias(t *testing.T) {
api := []*Route{
{"/users/:userID/following", ""},
{"/users/:userID/followedBy", ""},
{"/users/:userID/follow", ""},
}
testRouterAPI(t, api)
}
func TestRouterParamOrdering(t *testing.T) {
api := []*Route{
{"/:a/:b/:c/:id", ""},
{"/:a/:id", ""},
{"/:a/:g/:id", ""},
}
testRouterAPI(t, api)
api2 := []*Route{
{"/:a/:id", ""},
{"/:a/:g/:id", ""},
{"/:a/:b/:c/:id", ""},
}
testRouterAPI(t, api2)
api3 := []*Route{
{"/:a/:b/:c/:id", ""},
{"/:a/:g/:id", ""},
{"/:a/:id", ""},
}
testRouterAPI(t, api3)
}
func TestRouterMixedParams(t *testing.T) {
api := []*Route{
{"/teacher/:tid/room/suggestions", ""},
{"/teacher/:id", ""},
}
testRouterAPI(t, api)
api2 := []*Route{
{"/teacher/:id", ""},
{"/teacher/:tid/room/suggestions", ""},
}
testRouterAPI(t, api2)
}
func TestRouterParam1466(t *testing.T) {
is := is.New(t)
g := New()
r := g.router
r.add("/users/signup", func(c Context) error {
return nil
})
r.add("/users/signup/bulk", func(c Context) error {
return nil
})
r.add("/users/survey", func(c Context) error {
return nil
})
r.add("/users/:username", func(c Context) error {
return nil
})
r.add("/interests/:name/users", func(c Context) error {
return nil
})
r.add("/skills/:name/users", func(c Context) error {
return nil
})
// Additional routes for Issue 1479
r.add("/users/:username/likes/projects/ids", func(c Context) error {
return nil
})
r.add("/users/:username/profile", func(c Context) error {
return nil
})
r.add("/users/:username/uploads/:type", func(c Context) error {
return nil
})
c := g.newContext(nil, nil, "", nil).(*context)
r.find("/users/ajitem", c)
is.Equal("ajitem", c.Param("username"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/sharewithme", c)
is.Equal("sharewithme", c.Param("username"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/signup", c)
is.Equal("", c.Param("username"))
// Additional assertions for #1479
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/sharewithme/likes/projects/ids", c)
is.Equal("sharewithme", c.Param("username"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/ajitem/likes/projects/ids", c)
is.Equal("ajitem", c.Param("username"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/sharewithme/profile", c)
is.Equal("sharewithme", c.Param("username"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/ajitem/profile", c)
is.Equal("ajitem", c.Param("username"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/sharewithme/uploads/self", c)
is.Equal("sharewithme", c.Param("username"))
is.Equal("self", c.Param("type"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/ajitem/uploads/self", c)
is.Equal("ajitem", c.Param("username"))
is.Equal("self", c.Param("type"))
c = g.newContext(nil, nil, "", nil).(*context)
r.find("/users/tree/free", c)
is.Equal("", c.Param("id"))
is.Equal(Status(0), c.response.Status)
}
func benchmarkRouterRoutes(b *testing.B, routes []*Route) {
g := New()
r := g.router
b.ReportAllocs()
// Add routes
for _, route := range routes {
r.add(route.Path, func(c Context) error {
return nil
})
}
// Find routes
for i := 0; i < b.N; i++ {
for _, route := range gitHubAPI {
c := g.ctxpool.Get().(*context)
r.find(route.Path, c)
g.ctxpool.Put(c)
}
}
}
func BenchmarkRouterStaticRoutes(b *testing.B) {
benchmarkRouterRoutes(b, staticRoutes)
}
func BenchmarkRouterGitHubAPI(b *testing.B) {
benchmarkRouterRoutes(b, gitHubAPI)
}
func BenchmarkRouterParseAPI(b *testing.B) {
benchmarkRouterRoutes(b, parseAPI)
}
func BenchmarkRouterGooglePlusAPI(b *testing.B) {
benchmarkRouterRoutes(b, googlePlusAPI)
}
Source