Skip to content

Commit 39dbf3f

Browse files
vishrclaude
andcommitted
test: add rewritten group method-handling tests
The previous commit recorded only the deletion of the old file; this adds the rewritten suite (group_method_handling_test.go). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent be357cd commit 39dbf3f

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

group_method_handling_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: MIT
2+
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
3+
4+
package echo
5+
6+
import (
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
// These tests lock in v5's method-handling semantics for routes registered through
15+
// a Group. v5 resolves method mismatches (405) and OPTIONS at the router level and
16+
// does NOT register any implicit per-group catch-all route.
17+
//
18+
// They double as a regression gate. Registering a group-level catch-all — whether
19+
// manually via g.RouteNotFound("/*", ...) or automatically (as proposed in #2996 to
20+
// fix CORS-on-group preflight) — makes that catch-all match every method, which masks
21+
// both 405 and v5's automatic OPTIONS response as 404. Verified empirically: with such
22+
// a catch-all in place, "POST /api/users" returns 404 instead of 405 and
23+
// "OPTIONS /api/users" returns 404 instead of 204. If that behavior reaches this
24+
// branch, the first two tests below fail.
25+
26+
// A method mismatch on an existing group route must return 405 with the allowed
27+
// methods, not be masked to 404.
28+
func TestGroupRoute_methodMismatchReturns405(t *testing.T) {
29+
e := New()
30+
g := e.Group("/api")
31+
g.GET("/users", func(c *Context) error { return c.String(http.StatusOK, "users") })
32+
33+
req := httptest.NewRequest(http.MethodPost, "/api/users", nil)
34+
rec := httptest.NewRecorder()
35+
e.ServeHTTP(rec, req)
36+
37+
assert.Equal(t, http.StatusMethodNotAllowed, rec.Code,
38+
"POST to a GET-only group route must be 405, not masked to 404")
39+
assert.Equal(t, "OPTIONS, GET", rec.Header().Get(HeaderAllow),
40+
"405 response must advertise the allowed methods")
41+
}
42+
43+
// OPTIONS on an existing group route is answered automatically by Echo (204 +
44+
// Allow). This is the behavior CORS preflight relies on, so it must not be masked.
45+
func TestGroupRoute_automaticOPTIONS(t *testing.T) {
46+
e := New()
47+
g := e.Group("/api")
48+
g.GET("/users", func(c *Context) error { return c.String(http.StatusOK, "users") })
49+
50+
req := httptest.NewRequest(http.MethodOptions, "/api/users", nil)
51+
rec := httptest.NewRecorder()
52+
e.ServeHTTP(rec, req)
53+
54+
assert.Equal(t, http.StatusNoContent, rec.Code,
55+
"OPTIONS on a registered group route must be auto-answered (204), not masked to 404")
56+
assert.Equal(t, "OPTIONS, GET", rec.Header().Get(HeaderAllow),
57+
"automatic OPTIONS response must advertise the allowed methods")
58+
}
59+
60+
// A matched concrete route resolves to its own handler; only a genuinely unmatched
61+
// path under the prefix is a 404.
62+
func TestGroupRoute_concreteRoutesResolve(t *testing.T) {
63+
e := New()
64+
g := e.Group("/api")
65+
g.GET("/users", func(c *Context) error { return c.String(http.StatusOK, "users") })
66+
67+
status, body := request(http.MethodGet, "/api/users", e)
68+
assert.Equal(t, http.StatusOK, status)
69+
assert.Equal(t, "users", body)
70+
71+
status, _ = request(http.MethodGet, "/api/nope", e)
72+
assert.Equal(t, http.StatusNotFound, status)
73+
}
74+
75+
// A group prefix must not affect routing of routes registered outside the group.
76+
func TestGroup_doesNotAffectRootRoutes(t *testing.T) {
77+
e := New()
78+
e.GET("/health", func(c *Context) error { return c.String(http.StatusOK, "root") })
79+
g := e.Group("/api")
80+
g.GET("/users", func(c *Context) error { return c.String(http.StatusOK, "users") })
81+
82+
status, body := request(http.MethodGet, "/health", e)
83+
assert.Equal(t, http.StatusOK, status)
84+
assert.Equal(t, "root", body)
85+
}

0 commit comments

Comments
 (0)