Skip to content

Commit f4f7d88

Browse files
committed
test(router): add failing test to verify RouteNotFound parent-walk fix
This test verifies the fix for issue #2485 where RouteNotFound handlers registered at parent levels (root or groups) were not being found when a route exists but with wrong HTTP method. The test should: - FAIL on unpatched code (falls back to methodNotAllowedHandler) - PASS with the fix (finds parent's RouteNotFound handler) This demonstrates that the parent-walk logic correctly traverses up the node tree to find RouteNotFound handlers defined at higher levels. Signed-off-by: lyydsheep <2230561977@qq.com>
1 parent ec05bc8 commit f4f7d88

2 files changed

Lines changed: 57 additions & 5 deletions

File tree

router.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,12 +1011,28 @@ func (r *DefaultRouter) Route(c *Context) HandlerFunc {
10111011
rPath = matchedRouteMethod.Path
10121012
rHandler = matchedRouteMethod.handler
10131013
} else if currentNode.isHandler {
1014-
rInfo = methodNotAllowedRouteInfo
1014+
// Walk up the tree to find a parent's notFoundHandler
1015+
// This allows RouteNotFound handlers defined at a higher level in the tree
1016+
// (e.g., root level) to handle 404s for sub-paths (e.g., groups)
1017+
var parentNode *node
1018+
for parentNode = currentNode.parent; parentNode != nil; parentNode = parentNode.parent {
1019+
if parentNode.methods.notFoundHandler != nil {
1020+
matchedRouteMethod = parentNode.methods.notFoundHandler
1021+
rInfo = matchedRouteMethod.RouteInfo
1022+
rPath = matchedRouteMethod.Path
1023+
rHandler = matchedRouteMethod.handler
1024+
break
1025+
}
1026+
}
10151027

1016-
c.Set(ContextKeyHeaderAllow, currentNode.methods.allowHeader)
1017-
rHandler = r.methodNotAllowedHandler
1018-
if req.Method == http.MethodOptions {
1019-
rHandler = r.optionsMethodHandler
1028+
// If no parent notFoundHandler was found, use methodNotAllowedHandler
1029+
if rHandler == nil {
1030+
rInfo = methodNotAllowedRouteInfo
1031+
c.Set(ContextKeyHeaderAllow, currentNode.methods.allowHeader)
1032+
rHandler = r.methodNotAllowedHandler
1033+
if req.Method == http.MethodOptions {
1034+
rHandler = r.optionsMethodHandler
1035+
}
10201036
}
10211037
}
10221038
}

router_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3656,3 +3656,39 @@ func BenchmarkRouterGooglePlusAPIMisses(b *testing.B) {
36563656
func BenchmarkRouterParamsAndAnyAPI(b *testing.B) {
36573657
benchmarkRouterRoutes(b, paramAndAnyAPI, paramAndAnyAPIToFind)
36583658
}
3659+
3660+
// TestRouterRouteNotFoundParentWalkBug verifies the fix for issue #2485
3661+
// This test should FAIL on unpatched code and PASS with the fix
3662+
func TestRouterRouteNotFoundParentWalkBug(t *testing.T) {
3663+
e := New()
3664+
handlerID := ""
3665+
3666+
// Register a GET route inside a group
3667+
g := e.Group("/api")
3668+
g.GET("/users", func(c *Context) error {
3669+
handlerID = "users-get"
3670+
return nil
3671+
})
3672+
3673+
// Register RouteNotFound on the root at exact prefix "/api" (no wildcard)
3674+
// This is the critical scenario: without parent-walk fix, this handler won't be found
3675+
e.RouteNotFound("/api", func(c *Context) error {
3676+
handlerID = "root-not-found"
3677+
return nil
3678+
})
3679+
3680+
// Test case: request exists in router but with wrong HTTP method
3681+
// Without fix: should fall back to methodNotAllowedHandler (405)
3682+
// With fix: should find parent's RouteNotFound handler
3683+
req := httptest.NewRequest(http.MethodPost, "/api/users", nil)
3684+
rec := httptest.NewRecorder()
3685+
c := e.NewContext(req, rec)
3686+
handler := e.router.Route(c)
3687+
3688+
_ = handler(c)
3689+
3690+
// This assertion should FAIL on unpatched code (handlerID will be empty)
3691+
// and PASS with the fix (handlerID will be "root-not-found")
3692+
assert.Equal(t, "root-not-found", handlerID,
3693+
"Expected root RouteNotFound handler to fire for POST /api/users")
3694+
}

0 commit comments

Comments
 (0)