mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-03-19 14:17:48 +02:00
431 lines
7.5 KiB
Go
431 lines
7.5 KiB
Go
package router
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/hook"
|
|
)
|
|
|
|
func TestRouterGroupGroup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g0 := RouterGroup[*Event]{}
|
|
|
|
g1 := g0.Group("test1")
|
|
g2 := g0.Group("test2")
|
|
|
|
if total := len(g0.children); total != 2 {
|
|
t.Fatalf("Expected %d child groups, got %d", 2, total)
|
|
}
|
|
|
|
if g1.Prefix != "test1" {
|
|
t.Fatalf("Expected g1 with prefix %q, got %q", "test1", g1.Prefix)
|
|
}
|
|
if g2.Prefix != "test2" {
|
|
t.Fatalf("Expected g2 with prefix %q, got %q", "test2", g2.Prefix)
|
|
}
|
|
}
|
|
|
|
func TestRouterGroupBindFunc(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g := RouterGroup[*Event]{}
|
|
|
|
calls := ""
|
|
|
|
// append one function
|
|
g.BindFunc(func(e *Event) error {
|
|
calls += "a"
|
|
return nil
|
|
})
|
|
|
|
// append multiple functions
|
|
g.BindFunc(
|
|
func(e *Event) error {
|
|
calls += "b"
|
|
return nil
|
|
},
|
|
func(e *Event) error {
|
|
calls += "c"
|
|
return nil
|
|
},
|
|
)
|
|
|
|
if total := len(g.Middlewares); total != 3 {
|
|
t.Fatalf("Expected %d middlewares, got %v", 3, total)
|
|
}
|
|
|
|
for _, h := range g.Middlewares {
|
|
_ = h.Func(nil)
|
|
}
|
|
|
|
if calls != "abc" {
|
|
t.Fatalf("Expected calls sequence %q, got %q", "abc", calls)
|
|
}
|
|
}
|
|
|
|
func TestRouterGroupBind(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g := RouterGroup[*Event]{
|
|
// mock excluded middlewares to check whether the entry will be deleted
|
|
excludedMiddlewares: map[string]struct{}{"test2": {}},
|
|
}
|
|
|
|
calls := ""
|
|
|
|
// append one handler
|
|
g.Bind(&hook.Handler[*Event]{
|
|
Func: func(e *Event) error {
|
|
calls += "a"
|
|
return nil
|
|
},
|
|
})
|
|
|
|
// append multiple handlers
|
|
g.Bind(
|
|
&hook.Handler[*Event]{
|
|
Id: "test1",
|
|
Func: func(e *Event) error {
|
|
calls += "b"
|
|
return nil
|
|
},
|
|
},
|
|
&hook.Handler[*Event]{
|
|
Id: "test2",
|
|
Func: func(e *Event) error {
|
|
calls += "c"
|
|
return nil
|
|
},
|
|
},
|
|
)
|
|
|
|
if total := len(g.Middlewares); total != 3 {
|
|
t.Fatalf("Expected %d middlewares, got %v", 3, total)
|
|
}
|
|
|
|
for _, h := range g.Middlewares {
|
|
_ = h.Func(nil)
|
|
}
|
|
|
|
if calls != "abc" {
|
|
t.Fatalf("Expected calls %q, got %q", "abc", calls)
|
|
}
|
|
|
|
// ensures that the previously excluded middleware was removed
|
|
if len(g.excludedMiddlewares) != 0 {
|
|
t.Fatalf("Expected test2 to be removed from the excludedMiddlewares list, got %v", g.excludedMiddlewares)
|
|
}
|
|
}
|
|
|
|
func TestRouterGroupUnbind(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g := RouterGroup[*Event]{}
|
|
|
|
calls := ""
|
|
|
|
// anonymous middlewares
|
|
g.Bind(&hook.Handler[*Event]{
|
|
Func: func(e *Event) error {
|
|
calls += "a"
|
|
return nil // unused value
|
|
},
|
|
})
|
|
|
|
// middlewares with id
|
|
g.Bind(&hook.Handler[*Event]{
|
|
Id: "test1",
|
|
Func: func(e *Event) error {
|
|
calls += "b"
|
|
return nil // unused value
|
|
},
|
|
})
|
|
g.Bind(&hook.Handler[*Event]{
|
|
Id: "test2",
|
|
Func: func(e *Event) error {
|
|
calls += "c"
|
|
return nil // unused value
|
|
},
|
|
})
|
|
g.Bind(&hook.Handler[*Event]{
|
|
Id: "test3",
|
|
Func: func(e *Event) error {
|
|
calls += "d"
|
|
return nil // unused value
|
|
},
|
|
})
|
|
|
|
// remove
|
|
g.Unbind("") // should be no-op
|
|
g.Unbind("test1", "test3")
|
|
|
|
if total := len(g.Middlewares); total != 2 {
|
|
t.Fatalf("Expected %d middlewares, got %v", 2, total)
|
|
}
|
|
|
|
for _, h := range g.Middlewares {
|
|
if err := h.Func(nil); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if calls != "ac" {
|
|
t.Fatalf("Expected calls %q, got %q", "ac", calls)
|
|
}
|
|
|
|
// ensure that the ids were added in the exclude list
|
|
excluded := []string{"test1", "test3"}
|
|
if len(g.excludedMiddlewares) != len(excluded) {
|
|
t.Fatalf("Expected excludes %v, got %v", excluded, g.excludedMiddlewares)
|
|
}
|
|
for id := range g.excludedMiddlewares {
|
|
if !slices.Contains(excluded, id) {
|
|
t.Fatalf("Expected %q to be marked as excluded", id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRouterGroupRoute(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
group := RouterGroup[*Event]{}
|
|
|
|
sub := group.Group("sub")
|
|
|
|
var called bool
|
|
route := group.Route(http.MethodPost, "/test", func(e *Event) error {
|
|
called = true
|
|
return nil
|
|
})
|
|
|
|
// ensure that the route was registered only to the main one
|
|
// ---
|
|
if len(sub.children) != 0 {
|
|
t.Fatalf("Expected no sub children, got %d", len(sub.children))
|
|
}
|
|
|
|
if len(group.children) != 2 {
|
|
t.Fatalf("Expected %d group children, got %d", 2, len(group.children))
|
|
}
|
|
// ---
|
|
|
|
// check the registered route
|
|
// ---
|
|
if route != group.children[1] {
|
|
t.Fatalf("Expected group children %v, got %v", route, group.children[1])
|
|
}
|
|
|
|
if route.Method != http.MethodPost {
|
|
t.Fatalf("Expected route method %q, got %q", http.MethodPost, route.Method)
|
|
}
|
|
|
|
if route.Path != "/test" {
|
|
t.Fatalf("Expected route path %q, got %q", "/test", route.Path)
|
|
}
|
|
|
|
route.Action(nil)
|
|
if !called {
|
|
t.Fatal("Expected route action to be called")
|
|
}
|
|
}
|
|
|
|
func TestRouterGroupRouteAliases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
group := RouterGroup[*Event]{}
|
|
|
|
testErr := errors.New("test")
|
|
|
|
testAction := func(e *Event) error {
|
|
return testErr
|
|
}
|
|
|
|
scenarios := []struct {
|
|
route *Route[*Event]
|
|
expectMethod string
|
|
expectPath string
|
|
}{
|
|
{
|
|
group.Any("/test", testAction),
|
|
"",
|
|
"/test",
|
|
},
|
|
{
|
|
group.GET("/test", testAction),
|
|
http.MethodGet,
|
|
"/test",
|
|
},
|
|
{
|
|
group.SEARCH("/test", testAction),
|
|
"SEARCH",
|
|
"/test",
|
|
},
|
|
{
|
|
group.POST("/test", testAction),
|
|
http.MethodPost,
|
|
"/test",
|
|
},
|
|
{
|
|
group.DELETE("/test", testAction),
|
|
http.MethodDelete,
|
|
"/test",
|
|
},
|
|
{
|
|
group.PATCH("/test", testAction),
|
|
http.MethodPatch,
|
|
"/test",
|
|
},
|
|
{
|
|
group.PUT("/test", testAction),
|
|
http.MethodPut,
|
|
"/test",
|
|
},
|
|
{
|
|
group.HEAD("/test", testAction),
|
|
http.MethodHead,
|
|
"/test",
|
|
},
|
|
{
|
|
group.OPTIONS("/test", testAction),
|
|
http.MethodOptions,
|
|
"/test",
|
|
},
|
|
}
|
|
|
|
for i, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%d_%s_%s", i, s.expectMethod, s.expectPath), func(t *testing.T) {
|
|
if s.route.Method != s.expectMethod {
|
|
t.Fatalf("Expected method %q, got %q", s.expectMethod, s.route.Method)
|
|
}
|
|
|
|
if s.route.Path != s.expectPath {
|
|
t.Fatalf("Expected path %q, got %q", s.expectPath, s.route.Path)
|
|
}
|
|
|
|
if err := s.route.Action(nil); !errors.Is(err, testErr) {
|
|
t.Fatal("Expected test action")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRouterGroupHasRoute(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
group := RouterGroup[*Event]{}
|
|
|
|
group.Any("/any", nil)
|
|
|
|
group.GET("/base", nil)
|
|
group.DELETE("/base", nil)
|
|
|
|
sub := group.Group("/sub1")
|
|
sub.GET("/a", nil)
|
|
sub.POST("/a", nil)
|
|
|
|
sub2 := sub.Group("/sub2")
|
|
sub2.GET("/b", nil)
|
|
sub2.GET("/b/{test}", nil)
|
|
|
|
// special cases to test the normalizations
|
|
group.GET("/c/", nil) // the same as /c/{test...}
|
|
group.GET("/d/{test...}", nil) // the same as /d/
|
|
|
|
scenarios := []struct {
|
|
method string
|
|
path string
|
|
expected bool
|
|
}{
|
|
{
|
|
http.MethodGet,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"",
|
|
"/any",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodPost,
|
|
"/base",
|
|
false,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/base",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodDelete,
|
|
"/base",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/sub1",
|
|
false,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/sub1/a",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodPost,
|
|
"/sub1/a",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodDelete,
|
|
"/sub1/a",
|
|
false,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/sub2/b",
|
|
false,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/sub1/sub2/b",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/sub1/sub2/b/{test}",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/sub1/sub2/b/{test2}",
|
|
false,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/c/{test...}",
|
|
true,
|
|
},
|
|
{
|
|
http.MethodGet,
|
|
"/d/",
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.method+"_"+s.path, func(t *testing.T) {
|
|
has := group.HasRoute(s.method, s.path)
|
|
|
|
if has != s.expected {
|
|
t.Fatalf("Expected %v, got %v", s.expected, has)
|
|
}
|
|
})
|
|
}
|
|
}
|