@@ -3624,42 +3624,56 @@ func TestPathValues_GetOr(t *testing.T) {
36243624// TestRouterRouteNotFoundHandlerInGroup verifies that a RouteNotFound handler registered
36253625// at a parent (root or group) level is properly invoked for sub-paths when the route exists
36263626// but the HTTP method is not registered. This is the scenario described in issue #2485.
3627+ //
3628+ // The critical test cases use exact-prefix RouteNotFound registrations (not wildcard "/*")
3629+ // to exercise the parent-node walking fix. A RouteNotFound("/*") would create an anyKind
3630+ // node that the router already finds via normal backtracking even without the fix; the bug
3631+ // only manifests when the RouteNotFound is registered on an exact path or group prefix
3632+ // (no anyKind child), so the router must walk the parent chain to find it.
36273633func TestRouterRouteNotFoundHandlerInGroup (t * testing.T ) {
36283634 var testCases = []struct {
3629- name string
3630- whenMethod string
3631- whenURL string
3632- expectHandlerID string
3633- expectCode int
3634- registerRootNotFound bool
3635+ name string
3636+ whenMethod string
3637+ whenURL string
3638+ expectHandlerID string
3639+ registerRootNotFound bool
36353640 registerGroupNotFound bool
36363641 }{
36373642 {
3638- name : "root RouteNotFound handler fires for group sub-path with wrong method" ,
3639- whenMethod : http .MethodPost ,
3640- whenURL : "/api/users" ,
3641- expectHandlerID : "root-not-found" ,
3643+ // Root registers RouteNotFound on "/api" (exact prefix, no wildcard).
3644+ // Without the parent-walk fix the router falls back to methodNotAllowedHandler
3645+ // because the anyKind /* node does not exist and no backtracking reaches the
3646+ // root RouteNotFound. With the fix, the parent chain is walked from /api/users
3647+ // up to /api and the handler is found.
3648+ name : "root RouteNotFound on exact prefix fires for group sub-path with wrong method" ,
3649+ whenMethod : http .MethodPost ,
3650+ whenURL : "/api/users" ,
3651+ expectHandlerID : "root-not-found" ,
36423652 registerRootNotFound : true ,
36433653 },
36443654 {
3645- name : "group RouteNotFound handler fires for group sub-path with wrong method" ,
3655+ // Group registers its own RouteNotFound on "/api" (exact prefix, no wildcard).
3656+ // Same reasoning: parent-walk is required to find the handler.
3657+ name : "group RouteNotFound on exact prefix fires for group sub-path with wrong method" ,
36463658 whenMethod : http .MethodPost ,
36473659 whenURL : "/api/users" ,
36483660 expectHandlerID : "group-not-found" ,
36493661 registerGroupNotFound : true ,
36503662 },
36513663 {
3652- name : "root RouteNotFound fires when no group-level handler defined" ,
3653- whenMethod : http .MethodDelete ,
3654- whenURL : "/api/items/123" ,
3655- expectHandlerID : "root-not-found" ,
3664+ // Root registers RouteNotFound on "/api" (exact prefix).
3665+ // Wrong method on a param route inside the group still triggers the handler.
3666+ name : "root RouteNotFound fires for param sub-route with wrong method" ,
3667+ whenMethod : http .MethodDelete ,
3668+ whenURL : "/api/items/123" ,
3669+ expectHandlerID : "root-not-found" ,
36563670 registerRootNotFound : true ,
36573671 },
36583672 {
3659- name : "fallback to methodNotAllowed when no RouteNotFound handler defined" ,
3660- whenMethod : http . MethodPost ,
3661- whenURL : "/api/users" ,
3662- // no RouteNotFound registered → method not allowed
3673+ // No RouteNotFound registered anywhere → standard 405 methodNotAllowedHandler.
3674+ name : "fallback to methodNotAllowed when no RouteNotFound handler defined" ,
3675+ whenMethod : http . MethodPost ,
3676+ whenURL : "/api/users" ,
36633677 expectHandlerID : "method-not-allowed" ,
36643678 },
36653679 }
@@ -3669,7 +3683,7 @@ func TestRouterRouteNotFoundHandlerInGroup(t *testing.T) {
36693683 e := New ()
36703684 handlerID := ""
36713685
3672- // Register a GET route inside a group
3686+ // Register GET routes inside the /api group.
36733687 g := e .Group ("/api" )
36743688 g .GET ("/users" , func (c * Context ) error {
36753689 handlerID = "users-get"
@@ -3680,17 +3694,19 @@ func TestRouterRouteNotFoundHandlerInGroup(t *testing.T) {
36803694 return nil
36813695 })
36823696
3683- // Optionally register RouteNotFound at root level
3697+ // Register RouteNotFound on the exact group prefix "/api" (no trailing wildcard).
3698+ // This means the only way the router can find this handler when a sub-path is
3699+ // requested is by walking up the parent node tree — the scenario this fix covers.
36843700 if tc .registerRootNotFound {
3685- e .RouteNotFound ("/*" , func (c * Context ) error {
3701+ // Root-level handler registered on the group prefix path, not on "/*".
3702+ e .RouteNotFound ("/api" , func (c * Context ) error {
36863703 handlerID = "root-not-found"
36873704 return nil
36883705 })
36893706 }
3690-
3691- // Optionally register RouteNotFound at group level
36923707 if tc .registerGroupNotFound {
3693- g .RouteNotFound ("/*" , func (c * Context ) error {
3708+ // Group-level handler using the group's own prefix (no wildcard suffix).
3709+ g .RouteNotFound ("" , func (c * Context ) error {
36943710 handlerID = "group-not-found"
36953711 return nil
36963712 })
0 commit comments