package ginext import ( "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" ) func TestJoinPaths(t *testing.T) { cases := []struct { abs, rel, want string }{ {"", "", ""}, {"/api", "", "/api"}, {"/api", "users", "/api/users"}, {"/api/", "users", "/api/users"}, {"/api", "/users", "/api/users"}, {"/api/", "/users/", "/api/users/"}, {"/api", "users/", "/api/users/"}, {"", "/users", "/users"}, {"/", "/", "/"}, } for _, tc := range cases { got := joinPaths(tc.abs, tc.rel) if got != tc.want { t.Errorf("joinPaths(%q,%q)=%q want %q", tc.abs, tc.rel, got, tc.want) } } } func TestLastChar(t *testing.T) { if lastChar("hello") != 'o' { t.Fatalf("expected 'o'") } if lastChar("/") != '/' { t.Fatalf("expected '/'") } } func TestLastChar_PanicsOnEmpty(t *testing.T) { defer func() { if r := recover(); r == nil { t.Fatalf("expected panic on empty string") } }() lastChar("") } func sampleHandler(_ *gin.Context) {} func TestNameOfFunction(t *testing.T) { name := nameOfFunction(sampleHandler) if name == "" { t.Fatalf("expected non-empty name") } // nameOfFunction strips path prefix, expecting form "ginext.sampleHandler" or similar if name != "ginext.sampleHandler" { t.Errorf("expected ginext.sampleHandler, got %q", name) } } type sampleStruct struct{} func (s sampleStruct) Method(_ *gin.Context) {} func TestNameOfFunction_StripsFmSuffix(t *testing.T) { s := sampleStruct{} // Method values get a "-fm" suffix that nameOfFunction should strip name := nameOfFunction(s.Method) if name == "" { t.Fatalf("expected non-empty name") } if got := name; got[len(got)-3:] == "-fm" { t.Errorf("expected -fm suffix to be stripped, got %q", name) } } func TestRoutes_GroupAndAbsPath(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes() if rw.absPath != "" { t.Fatalf("expected empty absPath") } g1 := rw.Group("/api") if g1.absPath != "/api" { t.Fatalf("expected /api, got %q", g1.absPath) } g2 := g1.Group("/v1") if g2.absPath != "/api/v1" { t.Fatalf("expected /api/v1, got %q", g2.absPath) } } func TestRoutes_UseAccumulatesMiddleware(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes() mw1 := func(c *gin.Context) {} mw2 := func(c *gin.Context) {} r1 := rw.Use(mw1) if len(r1.defaultHandler) != 1 { t.Fatalf("expected 1 handler after Use, got %d", len(r1.defaultHandler)) } r2 := r1.Use(mw2) if len(r2.defaultHandler) != 2 { t.Fatalf("expected 2 handlers, got %d", len(r2.defaultHandler)) } // Original parent should be unchanged if len(rw.defaultHandler) != 0 { t.Fatalf("expected parent to remain unchanged, got %d", len(rw.defaultHandler)) } } func TestRoutes_GroupCopiesMiddleware(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes().Use(func(c *gin.Context) {}) g := rw.Group("/x") if len(g.defaultHandler) != 1 { t.Fatalf("expected group to inherit middleware") } } func TestRoutes_MethodBuilders(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes() cases := []struct { name string build func(string) *GinRouteBuilder want string }{ {"GET", rw.GET, http.MethodGet}, {"POST", rw.POST, http.MethodPost}, {"PUT", rw.PUT, http.MethodPut}, {"PATCH", rw.PATCH, http.MethodPatch}, {"DELETE", rw.DELETE, http.MethodDelete}, {"HEAD", rw.HEAD, http.MethodHead}, {"OPTIONS", rw.OPTIONS, http.MethodOptions}, {"COUNT", rw.COUNT, "COUNT"}, {"Any", rw.Any, "*"}, } for _, tc := range cases { b := tc.build("/foo") if b.method != tc.want { t.Errorf("%s: expected method %q, got %q", tc.name, tc.want, b.method) } if b.relPath != "/foo" { t.Errorf("%s: expected relPath /foo, got %q", tc.name, b.relPath) } } } func TestRoutes_RouteBuilderUseAppends(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes() b := rw.GET("/x") startCount := len(b.handlers) b.Use(func(c *gin.Context) {}) if len(b.handlers) != startCount+1 { t.Fatalf("expected handlers to grow by 1") } } func TestRoutes_RouteBuilderInheritsDefaultHandlers(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes().Use(func(c *gin.Context) {}) b := rw.GET("/x") if len(b.handlers) != 1 { t.Fatalf("expected route to inherit default handler") } } func TestRoutes_WithJSONFilter_AddsHandler(t *testing.T) { w := NewEngine(Options{}) rw := w.Routes().WithJSONFilter("xyz") if len(rw.defaultHandler) != 1 { t.Fatalf("expected json filter middleware to be added") } // invoke it to verify it sets the key rec := httptest.NewRecorder() c, _ := gin.CreateTestContext(rec) c.Request = httptest.NewRequest(http.MethodGet, "/", nil) rw.defaultHandler[0](c) if c.GetString(jsonFilterKey) != "xyz" { t.Fatalf("expected jsonFilterKey to be set to xyz") } } func TestRoutes_RouteBuilderWithJSONFilter(t *testing.T) { w := NewEngine(Options{}) b := w.Routes().GET("/x").WithJSONFilter("abc") if len(b.handlers) != 1 { t.Fatalf("expected handler to be added") } } func TestRoutes_HandleRegistersAndStoresSpec(t *testing.T) { w := NewEngine(Options{}) w.Routes().GET("/foo").Handle(func(p PreContext) HTTPResponse { return Status(http.StatusOK) }) if len(w.routeSpecs) != 1 { t.Fatalf("expected 1 route spec, got %d", len(w.routeSpecs)) } spec := w.routeSpecs[0] if spec.Method != http.MethodGet { t.Fatalf("expected GET, got %s", spec.Method) } if spec.URL != "/foo" { t.Fatalf("expected /foo, got %s", spec.URL) } // Hitting the route should serve our handler req := httptest.NewRequest(http.MethodGet, "/foo", nil) rec := w.ServeHTTP(req) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d", rec.Code) } } func TestRoutes_AnyRegistersAllMethods(t *testing.T) { w := NewEngine(Options{}) w.Routes().Any("/wild").Handle(func(p PreContext) HTTPResponse { return Status(http.StatusOK) }) for _, m := range anyMethods { req := httptest.NewRequest(m, "/wild", nil) rec := w.ServeHTTP(req) if rec.Code != http.StatusOK { t.Errorf("method %s: expected 200, got %d", m, rec.Code) } } if w.routeSpecs[0].Method != "ANY" { t.Fatalf("expected method label ANY, got %s", w.routeSpecs[0].Method) } } func TestRoutes_GroupedRoutes(t *testing.T) { w := NewEngine(Options{}) api := w.Routes().Group("/api") api.GET("/ping").Handle(func(p PreContext) HTTPResponse { return Text(http.StatusOK, "pong") }) req := httptest.NewRequest(http.MethodGet, "/api/ping", nil) rec := w.ServeHTTP(req) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d", rec.Code) } if rec.Body.String() != "pong" { t.Fatalf("expected pong, got %q", rec.Body.String()) } if w.routeSpecs[0].URL != "/api/ping" { t.Fatalf("expected absPath /api/ping in spec, got %s", w.routeSpecs[0].URL) } } func TestRoutes_NoRoute(t *testing.T) { w := NewEngine(Options{}) w.NoRoute(func(p PreContext) HTTPResponse { return Status(http.StatusTeapot) }) req := httptest.NewRequest(http.MethodGet, "/missing", nil) rec := w.ServeHTTP(req) if rec.Code != http.StatusTeapot { t.Fatalf("expected 418, got %d", rec.Code) } if len(w.routeSpecs) != 1 || w.routeSpecs[0].URL != "[NO_ROUTE]" { t.Fatalf("expected NO_ROUTE spec to be recorded") } }