summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--group.go50
-rw-r--r--group_test.go123
-rw-r--r--types.go5
3 files changed, 178 insertions, 0 deletions
diff --git a/group.go b/group.go
new file mode 100644
index 0000000..7cb56ad
--- /dev/null
+++ b/group.go
@@ -0,0 +1,50 @@
+package growel
+
+import (
+ "strings"
+)
+
+/* constructor */
+
+// Engine.Group creates a Group
+// pass the Group as a parameter to the callback function
+// used to create route groups
+func (e *Engine) Group(prefix string, fn func(*Group)) {
+ g := &Group{
+ prefix: prefix,
+ engine: e,
+ }
+
+ fn(g)
+}
+
+/* Inherit Engine methods */
+
+func (g *Group) GET(path string, handler Handler) {
+ full := joinPath(g.prefix, path)
+ g.engine.GET(full, handler)
+}
+
+func (g *Group) POST(path string, handler Handler) {
+ full := joinPath(g.prefix, path)
+ g.engine.POST(full, handler)
+}
+
+func (g *Group) DELETE(path string, handler Handler) {
+ full := joinPath(g.prefix, path)
+ g.engine.DELETE(full, handler)
+}
+
+func (g *Group) PUT(path string, handler Handler) {
+ full := joinPath(g.prefix, path)
+ g.engine.PUT(full, handler)
+}
+
+/* utils */
+
+func joinPath(a, b string) string {
+ if a == "/" {
+ return a + strings.TrimPrefix(b, "/")
+ }
+ return strings.TrimSuffix(a, "/") + "/" + strings.TrimPrefix(b, "/")
+}
diff --git a/group_test.go b/group_test.go
new file mode 100644
index 0000000..e682f09
--- /dev/null
+++ b/group_test.go
@@ -0,0 +1,123 @@
+package growel
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+/*
+ ======================================================
+ Test Group()
+ ======================================================
+*/
+
+func TestGroupCallback(t *testing.T) {
+ e := New()
+
+ called := false
+
+ e.Group("/user", func(g *Group) {
+ if g.prefix != "/user" {
+ t.Fatalf("expected prefix '/user', got '%s'", g.prefix)
+ }
+ if g.engine != e {
+ t.Fatal("group.engine does not point to Engine")
+ }
+ called = true
+ })
+
+ if !called {
+ t.Fatal("Group callback not invoked")
+ }
+}
+
+/*
+ ======================================================
+ Test route registration
+ ======================================================
+*/
+
+func TestGroupRouteRegistration(t *testing.T) {
+ e := New()
+
+ e.Group("/api", func(api *Group) {
+ api.GET("/ping", func(c *Context) {})
+ api.POST("/user", func(c *Context) {})
+ api.PUT("/v1/item/", func(c *Context) {})
+ api.DELETE("/v1/item/42", func(c *Context) {})
+ })
+
+ routes := e.router.routes
+
+ if len(routes) != 4 {
+ t.Fatalf("expected 4 routes, got %d", len(routes))
+ }
+
+ cases := []struct {
+ method string
+ path string
+ }{
+ {"GET", "/api/ping"},
+ {"POST", "/api/user"},
+ {"PUT", "/api/v1/item"},
+ {"DELETE", "/api/v1/item/42"},
+ }
+
+ for _, want := range cases {
+ found := false
+
+ for _, r := range routes {
+ joined := "/" + joinParts(r.parts)
+ if r.method == want.method && joined == want.path {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("missing expected route: %s %s", want.method, want.path)
+ }
+ }
+}
+
+// helper: convert []string{"api","ping"} → "api/ping"
+func joinParts(parts []string) string {
+ out := ""
+ for i, p := range parts {
+ if i > 0 {
+ out += "/"
+ }
+ out += p
+ }
+ return out
+}
+
+/*
+ ======================================================
+ End-to-End ServeHTTP test
+ ======================================================
+*/
+
+func TestGroupServeHTTP(t *testing.T) {
+ e := New()
+
+ e.Group("/hello", func(h *Group) {
+ h.GET("/world", func(c *Context) {
+ c.Plain(200, "ok")
+ })
+ })
+
+ w := httptest.NewRecorder()
+ req, _ := http.NewRequest("GET", "/hello/world", nil)
+
+ e.ServeHTTP(w, req)
+
+ if w.Code != 200 {
+ t.Fatalf("expected 200, got %d", w.Code)
+ }
+
+ if body := w.Body.String(); body != "ok\n" && body != "ok" {
+ t.Fatalf("expected body 'ok', got '%s'", body)
+ }
+}
diff --git a/types.go b/types.go
index 45bdf2c..df3e496 100644
--- a/types.go
+++ b/types.go
@@ -24,3 +24,8 @@ type Router struct {
type Engine struct {
router *Router
}
+
+type Group struct {
+ prefix string
+ engine *Engine
+}